File: process.rb

package info (click to toggle)
ruby-childprocess 4.1.0-3
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 376 kB
  • sloc: ruby: 2,541; makefile: 4
file content (131 lines) | stat: -rwxr-xr-x 2,836 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
module ChildProcess
  module Windows
    class Process < AbstractProcess

      attr_reader :pid

      def io
        @io ||= Windows::IO.new
      end

      def stop(timeout = 3)
        assert_started

        log "sending KILL"
        @handle.send(WIN_SIGKILL)

        poll_for_exit(timeout)
      ensure
        close_handle
        close_job_if_necessary
      end

      def wait
        if exited?
          exit_code
        else
          @handle.wait
          @exit_code = @handle.exit_code

          close_handle
          close_job_if_necessary

          @exit_code
        end
      end

      def exited?
        return true if @exit_code
        assert_started

        code   = @handle.exit_code
        exited = code != PROCESS_STILL_ACTIVE

        log(:exited? => exited, :code => code)

        if exited
          @exit_code = code
          close_handle
          close_job_if_necessary
        end

        exited
      end

      private

      def launch_process
        builder = ProcessBuilder.new(@args)
        builder.leader      = leader?
        builder.detach      = detach?
        builder.duplex      = duplex?
        builder.environment = @environment unless @environment.empty?
        builder.cwd         = @cwd

        if @io
          builder.stdout      = @io.stdout
          builder.stderr      = @io.stderr
        end

        @pid = builder.start
        @handle = Handle.open @pid

        if leader?
          @job = Job.new(detach?, true)
          @job << @handle
        end

        if duplex?
          raise Error, "no stdin stream" unless builder.stdin
          io._stdin = builder.stdin
        end

        self
      end

      def close_handle
        @handle.close
      end

      def close_job_if_necessary
        @job.close if leader?
      end


      class Job
        def initialize(detach, leader)
          @pointer = Lib.create_job_object(nil, nil)

          if @pointer.nil? || @pointer.null?
            raise Error, "unable to create job object"
          end

          basic = JobObjectBasicLimitInformation.new
          basic[:LimitFlags] |= JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE if !detach
          basic[:LimitFlags] |= JOB_OBJECT_LIMIT_BREAKAWAY_OK if leader

          extended = JobObjectExtendedLimitInformation.new
          extended[:BasicLimitInformation] = basic

          ret = Lib.set_information_job_object(
            @pointer,
            JOB_OBJECT_EXTENDED_LIMIT_INFORMATION,
            extended,
            extended.size
          )

          Lib.check_error ret
        end

        def <<(handle)
          Lib.check_error Lib.assign_process_to_job_object(@pointer, handle.pointer)
        end

        def close
          Lib.close_handle @pointer
        end
      end

    end # Process
  end # Windows
end # ChildProcess