NamedPipeInputStream.java
     1: //========================================================================================
     2: //  NamedPipeInputStream.java
     3: //    en:Input from named pipe
     4: //    ja:名前付きパイプから入力します
     5: //  Copyright (C) 2003-2025 Makoto Kamada
     6: //
     7: //  This file is part of the XEiJ (X68000 Emulator in Java).
     8: //  You can use, modify and redistribute the XEiJ if the conditions are met.
     9: //  Read the XEiJ License for more details.
    10: //  https://stdkmd.net/xeij/
    11: //========================================================================================
    12: 
    13: package xeij;
    14: 
    15: import java.io.*;
    16: import java.lang.foreign.*;  //Arena,FunctionDescriptor,Linker,MemorySegment,SymbolLookup,ValueLayout
    17: import java.lang.invoke.*;  //MethodHandle
    18: import java.util.*;  //NoSuchElementException
    19: 
    20: //class NamedPipeInputStream
    21: //  名前付きパイプInputStream
    22: public abstract class NamedPipeInputStream extends InputStream {
    23: 
    24:   //npis = NamedPipeInputStream.create (name)
    25:   //  コンストラクタ
    26:   //  npis  名前付きパイプInputStream
    27:   //  name  名前付きパイプの名前。プレフィックスを含まない
    28:   public static NamedPipeInputStream create (String name) throws IOException {
    29:     return System.getProperty ("os.name").indexOf ("Windows") < 0 ? new Gen (name) : new Win (name);
    30:   }  //open
    31: 
    32:   protected String path;  //パス。プレフィックスを含む
    33:   protected volatile boolean closed;  //true=すでに閉じた
    34:   protected volatile boolean connecting;  //true=接続待ちブロック中
    35:   protected volatile boolean reading;  //true=受信待ちブロック中
    36:   protected Thread thread;  //受信スレッド
    37:   protected ByteQueue queue;  //受信キュー
    38: 
    39:   //npis = new NamedPipeInputStream (name)
    40:   //  コンストラクタ
    41:   //  npis  名前付きパイプInputStream
    42:   //  name  名前付きパイプの名前。プレフィックスを含まない
    43:   private NamedPipeInputStream (String name) throws IOException {
    44:     //開始
    45:     osStart (name);
    46:     //受信キューを作る
    47:     queue = new ByteQueue ();
    48:     //受信スレッドを開始する
    49:     thread = new Thread () {
    50:       byte[] b = new byte[1024];
    51:       @Override public void run () {
    52:         try {
    53:           //開いて接続する
    54:           connecting = true;
    55:           osOpenAndConnect ();  //送信側が開くまでブロックする
    56:           connecting = false;
    57:           while (!closed) {
    58:             //受信する
    59:             reading = true;
    60:             int k = osRead (b);  //少なくとも1バイト受信するまでブロックする
    61:             reading = false;
    62:             if (closed) {
    63:               break;
    64:             }
    65:             if (k == -1) {  //EOFのとき
    66:               osClose ();  //直ちに接続し直す
    67:               connecting = true;
    68:               osOpenAndConnect ();  //送信側が開くまでブロックする
    69:               connecting = false;
    70:               continue;
    71:             }
    72:             //キューに書き込む
    73:             queue.write (b, 0, k);
    74:           }
    75:         } catch (IOException ioe) {
    76:           connecting = false;
    77:           reading = false;
    78:         }
    79:         //閉じる
    80:         osClose ();
    81:       }
    82:     };
    83:     thread.start ();
    84:     if (false) {
    85:       System.out.println ("named pipe " + path + " opened");
    86:     }
    87:   }  //NamedPipeInputStream
    88: 
    89:   //------------------------------------------------------------------------
    90:   //n = available ()
    91:   //  ブロックせずに受信できる長さを返す
    92:   //  n  ブロックせずに受信できる長さ
    93:   @Override
    94:   public int available () throws IOException {
    95:     //閉じていたら失敗
    96:     if (closed) {
    97:       throw new IOException ("named pipe " + path + " closed");
    98:     }
    99:     //ブロックせずに受信できる長さを返す
   100:     return queue.used ();
   101:   }  //available
   102: 
   103:   //------------------------------------------------------------------------
   104:   //close ()
   105:   //  名前付きパイプを閉じる
   106:   @Override
   107:   public void close () {
   108:     //閉じていたら何もしない
   109:     if (closed) {
   110:       return;
   111:     }
   112:     closed = true;
   113:     //ブロックを解除する
   114:     osUnblock ();
   115:     //閉じる
   116:     osClose ();
   117:     //受信スレッドの終了を待つ
   118:     try {
   119:       thread.join (100L);
   120:     } catch (InterruptedException ie) {
   121:     }
   122:     //終了
   123:     osEnd ();
   124:     //キューの読み出しをキャンセルする
   125:     queue.cancel ();  //waitAndReadのブロックを解く
   126:     if (false) {
   127:       System.out.println ("named pipe " + path + " closed");
   128:     }
   129:   }  //close
   130: 
   131:   //------------------------------------------------------------------------
   132:   //d = read ()
   133:   //  1バイト受信する。1バイト受信するまでブロックする
   134:   //  d  受信したデータ。0~255。-1=キャンセルされた
   135:   @Override
   136:   public int read () throws IOException {
   137:     //閉じていたら失敗
   138:     if (closed) {
   139:       throw new IOException ("named pipe " + path + " closed");
   140:     }
   141:     //キューから1バイト読み出す
   142:     return queue.waitAndRead ();  //1バイト読み出すまでブロックする
   143:   }  //read
   144: 
   145:   //------------------------------------------------------------------------
   146:   //k = read (b)
   147:   //  配列に受信する。少なくとも1バイト受信するまでブロックする
   148:   //  k  受信した長さ。-1=キャンセルされた
   149:   //  b  配列
   150:   @Override
   151:   public int read (byte[] b) throws IOException {
   152:     //閉じていたら失敗
   153:     if (closed) {
   154:       throw new IOException ("named pipe " + path + " closed");
   155:     }
   156:     //キューから配列に読み出す
   157:     return queue.waitAndRead (b, 0, b.length);  //少なくとも1バイト読み出すまでブロックする
   158:   }  //read
   159: 
   160:   //------------------------------------------------------------------------
   161:   //k = read (b, o, n)
   162:   //  配列に受信する。少なくとも1バイト受信するまでブロックする
   163:   //  k  受信した長さ。-1=キャンセルされた
   164:   //  b  配列
   165:   //  o  位置
   166:   //  n  長さ
   167:   @Override
   168:   public int read (byte[] b, int o, int n) throws IOException {
   169:     //閉じていたら失敗
   170:     if (closed) {
   171:       throw new IOException ("named pipe " + path + " closed");
   172:     }
   173:     //引数を確認する
   174:     if (o < 0 || n < 0 || b.length < o + n) {  //範囲外
   175:       throw new IndexOutOfBoundsException ("b.length=" + b.length + ", o=" + o + ", n=" + n);
   176:     }
   177:     //キューから配列に読み出す。少なくとも1バイト読み出すまでブロックする
   178:     return queue.waitAndRead (b, o, n);
   179:   }  //read
   180: 
   181:   //------------------------------------------------------------------------
   182:   //k = readNBytes (b, o, n)
   183:   //  配列に受信する。nバイト受信するまでブロックする
   184:   //  k  受信した長さ。-1=キャンセルされた
   185:   //  b  配列
   186:   //  o  位置
   187:   //  n  長さ
   188:   @Override
   189:   public int readNBytes (byte[] b, int o, int n) throws IOException {
   190:     //閉じていたら失敗
   191:     if (closed) {
   192:       throw new IOException ("named pipe " + path + " closed");
   193:     }
   194:     //引数を確認する
   195:     if (o < 0 || n < 0 || b.length < o + n) {  //範囲外
   196:       throw new IndexOutOfBoundsException ("b.length=" + b.length + ", o=" + o + ", n=" + n);
   197:     }
   198:     //キューから配列に読み出す
   199:     int k = 0;
   200:     while (k < n) {
   201:       int t = queue.waitAndRead (b, o + k, n - k);  //少なくとも1バイト読み出すまでブロックする
   202:       if (t == -1) {  //キャンセルされた
   203:         return -1;
   204:       }
   205:       k += t;
   206:     }
   207:     //受信した長さを返す
   208:     return k;
   209:   }  //readNBytes
   210: 
   211:   protected abstract void osStart (String name) throws IOException;
   212:   protected abstract void osOpenAndConnect () throws IOException;
   213:   protected abstract int osRead (byte[] b) throws IOException;
   214:   protected abstract void osUnblock ();
   215:   protected abstract void osClose ();
   216:   protected abstract void osEnd ();
   217: 
   218:   //class Gen
   219:   //  UNIX系OS用
   220:   private static class Gen extends NamedPipeInputStream {
   221: 
   222:     private File file;  //ファイル
   223:     private FileInputStream stream;  //FileInputStream。null=まだ開いていないまたはすでに閉じた
   224: 
   225:     //npis = new Gen (name)
   226:     //  コンストラクタ
   227:     protected Gen (String name) throws IOException {
   228:       super (name);
   229:     }  //Gen
   230: 
   231:     //osStart (name)
   232:     //  開始
   233:     @Override
   234:     protected void osStart (String name) throws IOException {
   235:       //パス
   236:       path = System.getProperty ("java.io.tmpdir") + "/" + name;  //通常は/tmp/~
   237:       //ファイル
   238:       file = new File (path);
   239:       //mkfifoでFIFOを作る
   240:       file.delete ();
   241:       Process process;
   242:       try {
   243:         process = new ProcessBuilder ("mkfifo", path).inheritIO ().start ();
   244:       } catch (IOException ioe) {  //start失敗。mkfifoを開始できなかった
   245:         throw new IOException ("mkfifo " + path + " not started");
   246:       }
   247:       try {
   248:         int exitCode = process.waitFor ();
   249:         if (exitCode != 0) {  //mkfifoがエラー終了した
   250:           file.delete ();
   251:           throw new IOException ("mkfifo " + path + " terminated with exit code " + exitCode);
   252:         }
   253:       } catch (InterruptedException ie) {  //waitFor中断。mkfifoの終了を確認できなかった
   254:         file.delete ();
   255:         throw new IOException ("mkfifo " + path + " interrupted");
   256:       }
   257:     }  //osStart
   258: 
   259:     //osOpenAndConnect ()
   260:     //  開いて接続する。送信側が開くまでブロックする
   261:     @Override
   262:     protected void osOpenAndConnect () throws IOException {
   263:       if (stream == null) {
   264:         stream = new FileInputStream (file);  //送信側が開くまでブロックする
   265:       }
   266:     }  //osOpenAndConnect
   267: 
   268:     //k = osRead (b)
   269:     //  受信する。少なくとも1バイト受信するまでブロックする
   270:     @Override
   271:     protected int osRead (byte[] b) throws IOException {
   272:       return stream.read (b);
   273:     }  //osRead
   274: 
   275:     //osUnblock ()
   276:     //  ブロックを解除する
   277:     @Override
   278:     protected void osUnblock () {
   279:       //new FileInputStream()のブロックを解除する
   280:       //  new FileInputStream()は送信側が開くまでブロックする。new FileOutputStream()でダミーの送信側を開くことで解除できる
   281:       //  new FileInputStream()でブロックしていないのにnew FileOutputStream()を行うとnew FileOutputStream()がブロックしてしまう可能性がある
   282:       if (connecting) {
   283:         try {
   284:           Thread.sleep (50L);
   285:         } catch (InterruptedException ie) {
   286:         }
   287:         if (connecting) {
   288:           try {
   289:             new FileOutputStream (file).close ();
   290:           } catch (IOException ioe) {
   291:           }
   292:           for (int i = 0; i < 100 && connecting; i++) {
   293:             try {
   294:               Thread.sleep (50L);
   295:             } catch (InterruptedException ie) {
   296:             }
   297:           }
   298:           if (connecting) {
   299:             System.out.println ("named pipe " + path + " unblocking timeout");
   300:           }
   301:         }
   302:       }
   303:       //FileInputStream.read()のブロックが解除されるまで待つ
   304:       //  FileInputStream.read()は送信側が開いたまま送信しないとブロックする。送信側が送信するか閉じるまでブロックを解除できない
   305:       if (reading) {
   306:         for (int i = 0; i < 100 && reading; i++) {
   307:           try {
   308:             Thread.sleep (50L);
   309:           } catch (InterruptedException ie) {
   310:           }
   311:         }
   312:         if (reading) {
   313:           System.out.println ("user operation required to unblock named pipe " + path);
   314:           while (reading) {
   315:             try {
   316:               Thread.sleep (1000L);
   317:             } catch (InterruptedException ie) {
   318:             }
   319:           }
   320:         }
   321:       }
   322:     }  //osUnblock
   323: 
   324:     //osClose ()
   325:     //  閉じる
   326:     @Override
   327:     protected void osClose () {
   328:       //開いていたら閉じる
   329:       if (stream != null) {
   330:         try {
   331:           stream.close ();
   332:         } catch (IOException ioe) {
   333:         }
   334:         stream = null;
   335:       }
   336:     }  //osClose
   337: 
   338:     //osEnd ()
   339:     //  終了
   340:     @Override
   341:     protected void osEnd () {
   342:       file.delete ();
   343:     }  //osEnd
   344: 
   345:   }  //class Gen
   346: 
   347:   //class Win
   348:   //  Windows用
   349:   private static class Win extends NamedPipeInputStream {
   350: 
   351:     //エラーコード
   352:     //  https://learn.microsoft.com/en-us/windows/win32/debug/system-error-codes
   353:     private static final int ERROR_BROKEN_PIPE = 109;  //パイプが終了した。誰かが開いて閉じた
   354:     private static final int ERROR_BAD_PIPE = 230;  //パイプの状態が無効。誰も開いていない
   355:     private static final int ERROR_NO_DATA = 232;  //既に切断された
   356:     private static final int ERROR_PIPE_CONNECTED = 535;  //既に接続している
   357:     private static final int ERROR_PIPE_LISTENING = 536;  //誰も開いていない
   358:     private static final int ERROR_OPERATION_ABORTED = 995;  //スレッドが終了したか操作が取り消された
   359:     private static final int ERROR_NOT_FOUND = 1168;  //取り消す操作がない
   360:     private static final long INVALID_HANDLE_VALUE = -1L;
   361: 
   362:     //リンカ
   363:     private Linker linker;
   364:     private MethodHandle downcallHandle (MemorySegment address, FunctionDescriptor function) {
   365:       return linker.downcallHandle (address, function);
   366:     }
   367: 
   368:     //アリーナ
   369:     private Arena arena;
   370: 
   371:     //メソッド
   372:     private MethodHandle CancelIoEx;
   373:     private MethodHandle CloseHandle;
   374:     private MethodHandle ConnectNamedPipe;
   375:     private static final int PIPE_ACCESS_INBOUND = 0x00000001;
   376:     private static final int PIPE_TYPE_BYTE = 0x00000000;
   377:     private static final int PIPE_WAIT = 0x00000000;
   378:     private static final int PIPE_UNLIMITED_INSTANCES = 255;
   379:     private static final int BUFFER_SIZE = 8192;
   380:     private MethodHandle CreateNamedPipeA;
   381:     private MethodHandle GetLastError;
   382:     private MethodHandle ReadFile;
   383: 
   384:     private MemorySegment handle;  //HANDLE。null=まだ開いていないまたはすでに閉じた
   385: 
   386:     //npis = new Win (name)
   387:     //  コンストラクタ
   388:     protected Win (String name) throws IOException {
   389:       super (name);
   390:     }  //Win
   391: 
   392:     //osStart (name)
   393:     //  開始
   394:     @Override
   395:     protected void osStart (String name) throws IOException {
   396:       //リンカ
   397:       linker = Linker.nativeLinker ();
   398:       //アリーナ
   399:       arena = Arena.ofAuto ();
   400:       //ライブラリ
   401:       SymbolLookup kernel32 = SymbolLookup.libraryLookup ("kernel32", arena);
   402:       //メソッド
   403:       try {
   404:         //CancelIoEx関数
   405:         //  https://learn.microsoft.com/ja-jp/windows/win32/fileio/cancelioex-func
   406:         CancelIoEx = downcallHandle (
   407:           kernel32.findOrThrow ("CancelIoEx"),
   408:           FunctionDescriptor.of (
   409:             ValueLayout.JAVA_INT,  //BOOL
   410:             ValueLayout.ADDRESS,  //HANDLE hFile
   411:             ValueLayout.ADDRESS));  //LPOVERLAPPED lpOverlapped
   412:         //CloseHandle関数
   413:         //  https://learn.microsoft.com/ja-jp/windows/win32/api/handleapi/nf-handleapi-closehandle
   414:         CloseHandle = downcallHandle (
   415:           kernel32.findOrThrow ("CloseHandle"),
   416:           FunctionDescriptor.of (
   417:             ValueLayout.JAVA_INT,  //BOOL
   418:             ValueLayout.ADDRESS));  //HANDLE hObject
   419:         //ConnectNamedPipe関数
   420:         //  https://learn.microsoft.com/ja-jp/windows/win32/api/namedpipeapi/nf-namedpipeapi-connectnamedpipe
   421:         ConnectNamedPipe = downcallHandle (
   422:           kernel32.findOrThrow ("ConnectNamedPipe"),
   423:           FunctionDescriptor.of (
   424:             ValueLayout.JAVA_INT,  //BOOL
   425:             ValueLayout.ADDRESS,  //HANDLE hNamedPipe
   426:             ValueLayout.ADDRESS));  //LPOVERLAPPED lpOverlapped
   427:         //CreateNamedPipeA関数
   428:         //  https://learn.microsoft.com/ja-jp/windows/win32/api/winbase/nf-winbase-createnamedpipea
   429:         CreateNamedPipeA = downcallHandle (
   430:           kernel32.findOrThrow ("CreateNamedPipeA"),
   431:           FunctionDescriptor.of (
   432:             ValueLayout.ADDRESS,  //HANDLE
   433:             ValueLayout.ADDRESS,  //LPCSTR lpName
   434:             ValueLayout.JAVA_INT,  //DWORD dwOpenMode
   435:             ValueLayout.JAVA_INT,  //DWORD dwPipeMode
   436:             ValueLayout.JAVA_INT,  //DWORD nMaxInstances
   437:             ValueLayout.JAVA_INT,  //DWORD nOutBufferSize
   438:             ValueLayout.JAVA_INT,  //DWORD nInBufferSize
   439:             ValueLayout.JAVA_INT,  //DWORD nDefaultTimeOut
   440:             ValueLayout.ADDRESS));  //LPSECURITY_ATTRIBUTES lpSecurityAttributes
   441:         //GetLastError関数
   442:         //  https://learn.microsoft.com/ja-jp/windows/win32/api/errhandlingapi/nf-errhandlingapi-getlasterror
   443:         GetLastError = downcallHandle (
   444:           kernel32.findOrThrow ("GetLastError"),
   445:           FunctionDescriptor.of (
   446:             ValueLayout.JAVA_INT));  //DWORD
   447:         //ReadFile関数
   448:         //  https://learn.microsoft.com/ja-jp/windows/win32/api/fileapi/nf-fileapi-readfilef
   449:         ReadFile = downcallHandle (
   450:           kernel32.findOrThrow ("ReadFile"),
   451:           FunctionDescriptor.of (
   452:             ValueLayout.JAVA_INT,  //BOOL
   453:             ValueLayout.ADDRESS,  //HANDLE hFile
   454:             ValueLayout.ADDRESS,  //LPVOID lpBuffer
   455:             ValueLayout.JAVA_INT,  //DWORD nNumberOfBytesToRead
   456:             ValueLayout.ADDRESS,  //LPDWORD lpNumberOfBytesRead
   457:             ValueLayout.ADDRESS));  //LPOVERLAPPED lpOverlapped
   458:       } catch (NoSuchElementException nsee) {
   459:         nsee.printStackTrace ();
   460:       }
   461:       //パス
   462:       path = "\\\\.\\pipe\\" + name;
   463:     }  //osStart
   464: 
   465:     //osOpenAndConnect ()
   466:     //  開いて接続する。送信側が開くまでブロックする
   467:     @Override
   468:     protected void osOpenAndConnect () throws IOException {
   469:       //閉じていたら開く
   470:       if (handle == null) {
   471:         try {
   472:           int error;
   473:           //  https://learn.microsoft.com/ja-jp/windows/win32/api/winbase/nf-winbase-createnamedpipea
   474:           if ((handle = (MemorySegment) CreateNamedPipeA.invoke (
   475:             arena.allocateFrom (path),  //LPCSTR lpName
   476:             PIPE_ACCESS_INBOUND,  //DWORD dwOpenMode
   477:             PIPE_TYPE_BYTE | PIPE_WAIT,  //DWORD dwPipeMode
   478:             PIPE_UNLIMITED_INSTANCES,  //DWORD nMaxInstances
   479:             0,  //DWORD nOutBufferSize
   480:             BUFFER_SIZE,  //DWORD nInBufferSize
   481:             0,  //DWORD nDefaultTimeOut
   482:             MemorySegment.NULL  //LPSECURITY_ATTRIBUTES lpSecurityAttributes
   483:             )).address () == INVALID_HANDLE_VALUE &&
   484:               (error = (int) GetLastError.invoke ()) != -1) {
   485:             handle = null;
   486:             //開けなかったら失敗
   487:             throw new IOException ("CreateNamedPipeA returned error code " + error);
   488:           }
   489:         } catch (IOException ioe) {
   490:           throw ioe;
   491:         } catch (Throwable e) {
   492:           e.printStackTrace ();
   493:           throw new IOException ("CreateNamedPipeA invocation failed");
   494:         }
   495:       }
   496:       //接続を待つ。送信側が開くまでブロックする
   497:       try {
   498:         int error;
   499:         //  https://learn.microsoft.com/ja-jp/windows/win32/api/namedpipeapi/nf-namedpipeapi-connectnamedpipe
   500:         if ((int) ConnectNamedPipe.invoke (
   501:           handle,  //HANDLE hNamedPipe
   502:           MemorySegment.NULL) == 0 &&  //LPOVERLAPPED lpOverlapped
   503:             (error = (int) GetLastError.invoke ()) != -1) {
   504:           if (error == 0 ||  //誰かがエラーコードを上書きした
   505:               error == ERROR_NO_DATA ||  //232 既に切断された
   506:               error == ERROR_PIPE_CONNECTED) {  //535 既に接続している
   507:           } else if (error == ERROR_OPERATION_ABORTED) {  //995 スレッドが終了したか操作が取り消された
   508:             throw new InterruptedIOException ("ConnectNamedPipe aborted");
   509:           } else {
   510:             throw new IOException ("ConnectNamedPipe returned error code " + error);
   511:           }
   512:         }
   513:       } catch (IOException ioe) {
   514:         throw ioe;
   515:       } catch (Throwable e) {
   516:         e.printStackTrace ();
   517:         throw new IOException ("ConnectNamedPipe invocation failed");
   518:       }
   519:     }  //osOpenAndConnect
   520: 
   521:     //k = osRead (b)
   522:     //  受信する。少なくとも1バイト受信するまでブロックする
   523:     @Override
   524:     protected int osRead (byte[] b) throws IOException {
   525:       int n = b.length;
   526:       MemorySegment buf = arena.allocate ((long) n);  //MemorySegment.ofArray(b)はasSlice(o+k)できない
   527:       int k = 0;
   528:       MemorySegment t = arena.allocate (ValueLayout.JAVA_INT);
   529:       t.set (ValueLayout.JAVA_INT, 0L, 0);
   530:       try {
   531:         while (k < 1) {
   532:           t.set (ValueLayout.JAVA_INT, 0L, 0);
   533:           int error;
   534:           //  https://learn.microsoft.com/ja-jp/windows/win32/api/fileapi/nf-fileapi-readfile
   535:           if ((int) ReadFile.invoke (
   536:             handle,  //HANDLE hFile
   537:             buf.asSlice ((long) k),  //LPVOID lpBuffer
   538:             n - k,  //DWORD nNumberOfBytesToRead
   539:             t,  //LPDWORD lpNumberOfBytesRead
   540:             MemorySegment.NULL) == 0 &&  //LPOVERLAPPED lpOverlapped
   541:               (error = (int) GetLastError.invoke ()) != -1) {
   542:             if (closed) {
   543:               throw new IOException ("named pipe " + path + " closed");
   544:             }
   545:             if (error == 0 ||  //誰かがエラーコードを上書きした
   546:                 error == ERROR_BROKEN_PIPE ||  //109 パイプが終了した。誰かが開いて閉じた
   547:                 error == ERROR_PIPE_LISTENING) {  //536 誰も開いていない
   548:               //0のとき閉じたのか取り消されたのかわからないが、後者はclosedなのでここには来ない
   549:               return -1;  //EOF
   550:             } else if (error == ERROR_OPERATION_ABORTED) {  //995 スレッドが終了したか操作が取り消された
   551:               throw new InterruptedIOException ("ReadFile aborted");
   552:             } else {
   553:               throw new IOException ("ReadFile returned error code " + error);
   554:             }
   555:           }
   556:           if (closed) {
   557:             throw new IOException ("named pipe " + path + " closed");
   558:           }
   559:           k += t.get (ValueLayout.JAVA_INT, 0L);
   560:         }
   561:       } catch (IOException ioe) {
   562:         throw ioe;
   563:       } catch (Throwable e) {
   564:         e.printStackTrace ();
   565:         throw new IOException ("ReadFile invocation failed");
   566:       }
   567:       System.arraycopy (buf.toArray (ValueLayout.JAVA_BYTE), 0, b, 0, k);
   568:       return k;
   569:     }  //osRead
   570: 
   571:     //osUnblock ()
   572:     //  ブロックを解除する
   573:     @Override
   574:     protected void osUnblock () {
   575:       //ConnectNamedPipeまたはReadFileのブロックを解除する
   576:       //  CancelIoExで取り消された操作はERROR_OPERATION_ABORTEDになる
   577:       //  MethodHandle.invoke()の問題で、取り消された操作がERROR_OPERATION_ABORTEDでなく0を受け取る可能性がある
   578:       if (connecting || reading) {
   579:         try {
   580:           Thread.sleep (50L);
   581:         } catch (InterruptedException ie) {
   582:         }
   583:         if (connecting) {
   584:           try {
   585:             int error;
   586:             //  https://learn.microsoft.com/ja-jp/windows/win32/fileio/cancelioex-func
   587:             if ((int) CancelIoEx.invoke (
   588:               handle,  //HANDLE hFile
   589:               MemorySegment.NULL) == 0 &&  //LPOVERLAPPED lpOverlapped
   590:                 (error = (int) GetLastError.invoke ()) != -1) {
   591:               //if (error == ERROR_NOT_FOUND) {  //1168 取り消す操作がない
   592:               //}
   593:             }
   594:           } catch (Throwable e) {
   595:             //e.printStackTrace ();
   596:             //throw new IOException ("CancelIoEx invocation failed");
   597:           }
   598:           for (int i = 0; i < 100 && (connecting || reading); i++) {
   599:             try {
   600:               Thread.sleep (50L);
   601:             } catch (InterruptedException ie) {
   602:             }
   603:           }
   604:           if (connecting || reading) {
   605:             System.out.println ("named pipe " + path + " unblocking timeout");
   606:           }
   607:         }
   608:       }
   609:     }  //osUnblock
   610: 
   611:     //osClose ()
   612:     //  閉じる
   613:     @Override
   614:     protected void osClose () {
   615:       //開いていたら閉じる
   616:       if (handle != null) {
   617:         try {
   618:           int error;
   619:           //  https://learn.microsoft.com/ja-jp/windows/win32/api/handleapi/nf-handleapi-closehandle
   620:           if ((int) CloseHandle.invoke (
   621:             handle) == 0 &&  //HANDLE hObject
   622:               (error = (int) GetLastError.invoke ()) != -1) {
   623:             //throw new IOException ("CloseHandle returned error code " + error);
   624:           }
   625:           //} catch (IOException ioe) {
   626:           //  throw ioe;
   627:         } catch (Throwable e) {
   628:           e.printStackTrace ();
   629:           //throw new IOException ("CloseHandle invocation failed");
   630:         }
   631:         handle = null;
   632:       }
   633:     }  //osClose
   634: 
   635:     //osEnd ()
   636:     //  終了
   637:     @Override
   638:     protected void osEnd () {
   639:     }  //osEnd
   640: 
   641:   }  //class Win
   642: 
   643: }  //class NamedPipeInputStream