javalinuxpidisaliveprocesshandle

ProcessHandle returns ambiguous results


I've two Java 11 methods that check whether a process for a given PID is running:

  public static final boolean isProcessRunning( final long pid ) {
    Optional< ProcessHandle > optionalProcess = ProcessHandle.of( pid );
    return optionalProcess.isPresent() && optionalProcess.get().isAlive();
  }

  public static boolean isProcessRunning2( final long pid ) {
    return ProcessHandle.allProcesses()
        .filter( p -> p.pid() == pid )
        .anyMatch( p -> p.isAlive() );
  }

When checking multiple PIDs on Linux clients, I'm getting sometimes different results for certain PIDs, where the first implementation always returns true and the second one always false.

Checking some "false positives" with shell command ps -ef | grep <pid> shows that first implementation seems to be wrong, the OS doesn't know about these processes neither. Assumably the second implementation is always right but seems to be very inefficient.

What's wrong with the first implementation and how can I fix it?


Solution

  • Having a look at the implementation of ProcessHandleImpl.isAlive() (Java 11 version) it seems that this method might return true if the native method isAlive0(pid) returns 0 - which it does "if the start time cannot be determined". The same native method is also used to determine whether to return an empty optional (returns -1) or not (returns 0 or actual start time). ProcessHandle.allProcesses() on the other hand gets all children of pid 0 and then calls getProcessPids0(...) (another native method) to return the pids and start times.

    So it seems to be a difference in the native code of the JVM you're using - or one in your OS (depends on what the native code is doing).

    To "fix" your first snippet, you could eliminate the "0" start times by using info().startInstant().isPresent():

    public static final boolean isProcessRunning( final long pid, final boolean excludeUnsure ) {
        Optional< ProcessHandle > optionalProcess = ProcessHandle.of( pid );
        
        if( excludeUnsure ) {
            return optionalProcess.map(ph -> ph.info())
                    .flatMap(info -> info.startInstant())
                    .isPresent();
        } else {
            return optionalProcess.map(ph -> ph.isAlive()).orElse(Boolean.FALSE);
        }      
     }