Skip to content

[Process] Provide getPipes() public interface. #19559

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from

Conversation

boombatower
Copy link
Contributor

Q A
Branch? master
Bug fix? no
New feature? yes
BC breaks? no
Deprecations? no
Tests pass? yes
License MIT

Useful for performing stream operations directly on the pipes.

@nicolas-grekas
Copy link
Member

But this isn't portable unless I'm wrong: Windows pipes or sigchild enabled php pipes must be handled very differently. This looks like making the abstraction leak and defeating the purpose of the component.
What do you need this pipes for?

@boombatower
Copy link
Contributor Author

@nicolas-grekas Could be, but then not sure what is the correct way to expose the unix pipes.

As the comment in code indicates I need to be able to use stream_select() to wait for output from a number of processes in a loop with a timeout. During timeouts there are other things that need to be done (ie processing). This is independent of the idle timeout provided by Process.

Additionally, per the discussion in #19558 I also need direct write access to input pipe to avoid delays. Alternatively, write could be handled by calling isrunning() (which triggers readwrite or pipes) after appendingInputBuffer(), but as described the current interactive input implementation is unclear how to flush the buffer.

Useful for performing stream operations directly on the pipes.
@boombatower
Copy link
Contributor Author

boombatower commented Aug 7, 2016

As far as portability getPipes() returns the wrapper pipe class which will expose different features on different platforms. As such it's portable, but one would need to check the specific subclass to know if particular features are available.

Having a parameter for the stream_select() timeout on readAndWrite() could then be used directly which solves my case for a single process, but unfortunately does not help for handling multiple.

For now I'll just use it this way until someone can come up with an acceptable way.

@boombatower
Copy link
Contributor Author

boombatower commented Aug 7, 2016

To be clear I basically need:

while (true) {
  $r = [$process1->getPipes()->pipes[1], $process2->getPipes()->pipes[1]];
  stream_select($r, $w, $e, 1, 0);
}

Noting the custom timeout. With the rest of feature provided by process which I can trigger with:

    $process->isRunning(); // triggers reading of pipes
    $process->checkTimeout(); // can trigger exception

Many moons ago, I did this with php-inotify while watching multiple files as whichever was written to first indicated completion. No need for ReactPHP or somesuch as that's the whole point of @stream_select(). Simply waiting for one of multiple streams to be updated, no need for concurrent processing.

@boombatower
Copy link
Contributor Author

boombatower commented Aug 9, 2016

@nicolas-grekas Also what do you think of exposing processInformation?

For instance if one wants to handle process termination inside a process subclass the only real place is updateStatus (which is protected), but calling any message such as isRunning() results in an infinite loop since they all call updateStatus. The only real way around this is direct access to the variables they read .

updateStatus()
{
  $running = magic();
  parent::updateStatus()
  if (!magic() && $running) {
    // terminated
  }
}

I understand the concept behind only exposing the API surface one intends, but having everything private without a properly thought through flexible API just neuters an otherwise handy implementation.

Any thoughts?

@nicolas-grekas
Copy link
Member

nicolas-grekas commented Aug 9, 2016

So, I'm 👎 on this one: the exposed classes are @internal. I understand this provides no solution to the stream_select use case. If you want to make your code take responsibility for this, you could access to the pipes through the private properties (through e.g. a rebound closure).

About your last question, you should be able already to deal with recursivity: just add a property isRecursiveCall and don't call the parent on recursive calls. WDYT?

@boombatower
Copy link
Contributor Author

boombatower commented Aug 10, 2016

The Closure::bind() works, thanks for the suggestion. In case anyone else wants an example, I have a subclass of Process with with the following:

/**
 * Returns PipesInterface for underlying process.
 *
 * Useful for performing stream operations directly on the pipes. For
 * example performing a stream_select() across multiple processes.
 *
 * Obviously, closing or altering pipes may cause errors.
 *
 * @throws LogicException In case the process is not running
 */
public function getPipes()
{
  static $bind;

  if (!isset($bind)) {
    $getPipes = function()
    {
      if (!$this->isRunning()) {
        throw new LogicException('Process pipes are not available while the process is not running.');
      }

      return $this->processPipes;
    };
    // The scope parameter will need to be updated if this class ends up with
    // another parent in between it and Symfony Process.
    $bind = Closure::bind($getPipes, $this, get_parent_class());
  }

  return $bind();
}

The recursive check I implemented as method static, but it indeed functions. Thanks. I prefer these to having a fork/patch against proper Process.

As such I imagine these pull requests can be closed

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants