win32-job 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 616cc46c7f759058ebeb168cb3bc4056ad7f6a45
4
- data.tar.gz: 1b11d76a96473a5329595841e75385a14c6444cf
3
+ metadata.gz: b05ef073cbab8e2e228f1bbe4dc4691d42d7e161
4
+ data.tar.gz: e8b44cab6624612b066f785e9895851384c3e963
5
5
  SHA512:
6
- metadata.gz: 19e1fc1b49f54627771f717ad838a013409ec3cd13f667036aaf06c9113c8de0d82f95c378a23248460eb2f14cdfb13f85cc6a9f15069b1da4d03596a97d07a3
7
- data.tar.gz: 7d2bd91f2104817cd31197c8f65815af65b88449bf96a3c48604c7a6f43ec8d61980c8115beee9dc2cd14a204f6ccafe5ad0fc2e01f0fddfda63338be4ab02bd
6
+ metadata.gz: ad0a9b6601e876eb373e710fa900d2033ff45e2ab06a5dc1f18438ecf2dd9a71fff549e4eeb621a1c8c82d23bbed258d9c5aba8845d2732187e0f8604055dfe1
7
+ data.tar.gz: 977ff9aadaa433efa20a15637f51bdf13c2b2e72aea844543cf9a5aed99d43c2343d6a52145d7a883830be18729db76d86a7ed31c791dcdfa69ba826f9c05e09
data/CHANGES CHANGED
@@ -1,2 +1,5 @@
1
- = 0.1.0 - 4-Feb-2014
2
- * Initial release.
1
+ = 0.1.1 - 5-Feb-2014
2
+ * Added a wait method.
3
+
4
+ = 0.1.0 - 4-Feb-2014
5
+ * Initial release.
data/MANIFEST CHANGED
@@ -1,12 +1,12 @@
1
- * CHANGES
2
- * MANIFEST
3
- * Rakefile
4
- * README
5
- * win32-job.gemspec
6
- * examples\example_job.rb
7
- * lib\win32\job.rb
8
- * lib\win32\job\constants.rb
9
- * lib\win32\job\functions.rb
10
- * lib\win32\job\helper.rb
11
- * lib\win32\job\structs.rb
12
- * test\test_win32_job.rb
1
+ * CHANGES
2
+ * MANIFEST
3
+ * Rakefile
4
+ * README
5
+ * win32-job.gemspec
6
+ * examples\example_job.rb
7
+ * lib\win32\job.rb
8
+ * lib\win32\job\constants.rb
9
+ * lib\win32\job\functions.rb
10
+ * lib\win32\job\helper.rb
11
+ * lib\win32\job\structs.rb
12
+ * test\test_win32_job.rb
data/README CHANGED
@@ -5,7 +5,8 @@
5
5
  gem install win32-job
6
6
 
7
7
  == Prerequisites
8
- ffi
8
+ * ffi
9
+ * Ruby 1.9.3 or later
9
10
 
10
11
  == Synopsis
11
12
  require 'win32/job'
@@ -25,9 +26,6 @@
25
26
 
26
27
  job.close
27
28
 
28
- == Future Plans
29
- Add a wait method that waits for all processes to complete.
30
-
31
29
  == Known Issues
32
30
  None known.
33
31
 
@@ -38,7 +36,7 @@
38
36
  Artistic 2.0
39
37
 
40
38
  == Copyright
41
- (C) 2003-2013 Daniel J. Berger
39
+ (C) 2003-2014 Daniel J. Berger
42
40
  All Rights Reserved
43
41
 
44
42
  == Warranty
@@ -46,5 +44,6 @@
46
44
  implied warranties, including, without limitation, the implied
47
45
  warranties of merchantability and fitness for a particular purpose.
48
46
 
49
- == Author
47
+ == Authors
50
48
  Daniel J. Berger
49
+ Park Heesob
data/Rakefile CHANGED
@@ -1,31 +1,31 @@
1
- require 'rake'
2
- require 'rake/clean'
3
- require 'rake/testtask'
4
-
5
- CLEAN.include("**/*.gem", "**/*.rbc", "**/*.rbx")
6
-
7
- namespace :gem do
8
- desc 'Create the win32-job gem'
9
- task :create do
10
- spec = eval(IO.read('win32-job.gemspec'))
11
- if Gem::VERSION < "2.0"
12
- Gem::Builder.new(spec).build
13
- else
14
- require 'rubygems/package'
15
- Gem::Package.build(spec)
16
- end
17
- end
18
-
19
- desc 'Install the win32-job gem'
20
- task :install => [:create] do
21
- file = Dir["*.gem"].first
22
- sh "gem install #{file}"
23
- end
24
- end
25
-
26
- Rake::TestTask.new do |t|
27
- t.verbose = true
28
- t.warning = true
29
- end
30
-
31
- task :default => :test
1
+ require 'rake'
2
+ require 'rake/clean'
3
+ require 'rake/testtask'
4
+
5
+ CLEAN.include("**/*.gem", "**/*.rbc", "**/*.rbx")
6
+
7
+ namespace :gem do
8
+ desc 'Create the win32-job gem'
9
+ task :create do
10
+ spec = eval(IO.read('win32-job.gemspec'))
11
+ if Gem::VERSION < "2.0"
12
+ Gem::Builder.new(spec).build
13
+ else
14
+ require 'rubygems/package'
15
+ Gem::Package.build(spec)
16
+ end
17
+ end
18
+
19
+ desc 'Install the win32-job gem'
20
+ task :install => [:create] do
21
+ file = Dir["*.gem"].first
22
+ sh "gem install #{file}"
23
+ end
24
+ end
25
+
26
+ Rake::TestTask.new do |t|
27
+ t.verbose = true
28
+ t.warning = true
29
+ end
30
+
31
+ task :default => :test
@@ -1,29 +1,29 @@
1
- #######################################################################
2
- # example_job.rb
3
- #
4
- # Simple example script for futzing with Windows Jobs. This will fire
5
- # up two instances of notepad and add them to the job, then close
6
- # them.
7
- #######################################################################
8
- require 'win32/job'
9
- include Win32
10
-
11
- pid1 = Process.spawn("notepad.exe")
12
- pid2 = Process.spawn("notepad.exe")
13
- sleep 0.5
14
-
15
- j = Job.new('test')
16
-
17
- j.configure_limit(
18
- :breakaway_ok => true,
19
- :kill_on_job_close => true,
20
- :process_memory => 1024 * 8,
21
- :process_time => 1000
22
- )
23
-
24
- j.add_process(pid1)
25
- j.add_process(pid2)
26
-
27
- sleep 0.5
28
-
29
- j.close # Notepad instances should terminate here, too.
1
+ #######################################################################
2
+ # example_job.rb
3
+ #
4
+ # Simple example script for futzing with Windows Jobs. This will fire
5
+ # up two instances of notepad and add them to the job, then close
6
+ # them.
7
+ #######################################################################
8
+ require 'win32/job'
9
+ include Win32
10
+
11
+ pid1 = Process.spawn("notepad.exe")
12
+ pid2 = Process.spawn("notepad.exe")
13
+ sleep 0.5
14
+
15
+ j = Job.new('test')
16
+
17
+ j.configure_limit(
18
+ :breakaway_ok => true,
19
+ :kill_on_job_close => true,
20
+ :process_memory => 1024 * 8,
21
+ :process_time => 1000
22
+ )
23
+
24
+ j.add_process(pid1)
25
+ j.add_process(pid2)
26
+
27
+ sleep 0.5
28
+
29
+ j.close # Notepad instances should terminate here, too.
data/lib/win32/job.rb CHANGED
@@ -14,7 +14,7 @@ module Win32
14
14
  extend Windows::Functions
15
15
 
16
16
  # The version of the win32-job library
17
- VERSION = '0.1.0'
17
+ VERSION = '0.1.1'
18
18
 
19
19
  private
20
20
 
@@ -448,10 +448,6 @@ module Win32
448
448
  #--
449
449
  # See http://blogs.msdn.com/b/oldnewthing/archive/2013/04/05/10407778.aspx
450
450
  #
451
- # TODO: Fix. I'm not sure this approach is feasible without the processes
452
- # having been created in a suspended state.
453
- #
454
- =begin
455
451
  def wait
456
452
  io_port = CreateIoCompletionPort(INVALID_HANDLE_VALUE, 0, 0, 1)
457
453
 
@@ -472,31 +468,15 @@ module Win32
472
468
 
473
469
  FFI.raise_windows_error('SetInformationJobObject', FFI.errno) unless bool
474
470
 
475
- @process_list.each{ |pid|
476
- handle = OpenProcess(PROCESS_SET_QUOTA|PROCESS_TERMINATE, false, pid)
477
-
478
- FFI.raise_windows_error('OpenProcess', FFI.errno) unless handle
479
-
480
- # BUG: I get access denied errors here.
481
- unless AssignProcessToJobObject(@job_handle, handle)
482
- FFI.raise_windows_error('AssignProcessToJobObject', FFI.errno)
483
- end
484
-
485
- # TODO: Do I need to get the thread handle and explicitly close it?
471
+ olap = FFI::MemoryPointer.new(Overlapped)
472
+ bytes = FFI::MemoryPointer.new(:ulong)
473
+ ckey = FFI::MemoryPointer.new(:uintptr_t)
486
474
 
487
- CloseHandle(handle)
488
-
489
- olap = OVERLAPPED.new
490
- bytes = FFI::MemoryPointer.new(:ulong)
491
- ckey = FFI::MemoryPointer.new(:uintptr_t)
492
-
493
- while GetQueuedCompletionPort(io_port, bytes, ckey, olap, INFINITE) &&
494
- !(ckey.read_pointer.to_i == @job_handle && ccode == JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO)
495
- sleep 0.1
496
- end
497
- }
475
+ while GetQueuedCompletionStatus(io_port, bytes, ckey, olap, INFINITE) &&
476
+ !(ckey.read_pointer.to_i == @job_handle && bytes.read_ulong == JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO)
477
+ sleep 0.1
478
+ end
498
479
  end
499
- =end
500
480
 
501
481
  private
502
482
 
@@ -1,64 +1,64 @@
1
- require 'ffi'
2
-
3
- module Windows
4
- module Constants
5
- private
6
-
7
- JobObjectBasicAccountingInformation = 1
8
- JobObjectBasicLimitInformation = 2
9
- JobObjectBasicProcessIdList = 3
10
- JobObjectBasicUIRestrictions = 4
11
- JobObjectSecurityLimitInformation = 5
12
- JobObjectEndOfJobTimeInformation = 6
13
- JobObjectAssociateCompletionPortInformation = 7
14
- JobObjectBasicAndIoAccountingInformation = 8
15
- JobObjectExtendedLimitInformation = 9
16
- JobObjectGroupInformation = 11
17
- JobObjectNotificationLimitInformation = 12
18
- JobObjectLimitViolationInformation = 13
19
- JobObjectGroupInformationEx = 14
20
- JobObjectCpuRateControlInformation = 15
21
-
22
- PROCESS_ALL_ACCESS = 0x1F0FFF
23
- PROCESS_SET_QUOTA = 0x0100
24
- PROCESS_TERMINATE = 0x0001
25
- SYNCHRONIZE = 0x00100000
26
-
27
- INFINITE = 0xFFFFFFFF
28
-
29
- INVALID_HANDLE_VALUE = FFI::Pointer.new(-1).address
30
-
31
- JOB_OBJECT_LIMIT_ACTIVE_PROCESS = 0x00000008
32
- JOB_OBJECT_LIMIT_AFFINITY = 0x00000010
33
- JOB_OBJECT_LIMIT_BREAKAWAY_OK = 0x00000800
34
- JOB_OBJECT_LIMIT_DIE_ON_UNHANDLED_EXCEPTION = 0x00000400
35
- JOB_OBJECT_LIMIT_JOB_MEMORY = 0x00000200
36
- JOB_OBJECT_LIMIT_JOB_TIME = 0x00000004
37
- JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE = 0x00002000
38
- JOB_OBJECT_LIMIT_PRESERVE_JOB_TIME = 0x00000040
39
- JOB_OBJECT_LIMIT_PRIORITY_CLASS = 0x00000020
40
- JOB_OBJECT_LIMIT_PROCESS_MEMORY = 0x00000100
41
- JOB_OBJECT_LIMIT_PROCESS_TIME = 0x00000002
42
- JOB_OBJECT_LIMIT_SCHEDULING_CLASS = 0x00000080
43
- JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK = 0x00001000
44
- JOB_OBJECT_LIMIT_SUBSET_AFFINITY = 0x00004000
45
- JOB_OBJECT_LIMIT_WORKINGSET = 0x00000001
46
-
47
- JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO = 4
48
-
49
- WAIT_ABANDONED = 0x00000080
50
- WAIT_IO_COMPLETION = 0x000000C0
51
- WAIT_OBJECT_0 = 0x00000000
52
- WAIT_TIMEOUT = 0x00000102
53
- WAIT_FAILED = 0xFFFFFFFF
54
-
55
- public
56
-
57
- ABOVE_NORMAL_PRIORITY_CLASS = 0x00008000
58
- BELOW_NORMAL_PRIORITY_CLASS = 0x00004000
59
- HIGH_PRIORITY_CLASS = 0x00000080
60
- IDLE_PRIORITY_CLASS = 0x00000040
61
- NORMAL_PRIORITY_CLASS = 0x00000020
62
- REALTIME_PRIORITY_CLASS = 0x00000100
63
- end
64
- end
1
+ require 'ffi'
2
+
3
+ module Windows
4
+ module Constants
5
+ private
6
+
7
+ JobObjectBasicAccountingInformation = 1
8
+ JobObjectBasicLimitInformation = 2
9
+ JobObjectBasicProcessIdList = 3
10
+ JobObjectBasicUIRestrictions = 4
11
+ JobObjectSecurityLimitInformation = 5
12
+ JobObjectEndOfJobTimeInformation = 6
13
+ JobObjectAssociateCompletionPortInformation = 7
14
+ JobObjectBasicAndIoAccountingInformation = 8
15
+ JobObjectExtendedLimitInformation = 9
16
+ JobObjectGroupInformation = 11
17
+ JobObjectNotificationLimitInformation = 12
18
+ JobObjectLimitViolationInformation = 13
19
+ JobObjectGroupInformationEx = 14
20
+ JobObjectCpuRateControlInformation = 15
21
+
22
+ PROCESS_ALL_ACCESS = 0x1F0FFF
23
+ PROCESS_SET_QUOTA = 0x0100
24
+ PROCESS_TERMINATE = 0x0001
25
+ SYNCHRONIZE = 0x00100000
26
+
27
+ INFINITE = 0xFFFFFFFF
28
+
29
+ INVALID_HANDLE_VALUE = FFI::Pointer.new(-1).address
30
+
31
+ JOB_OBJECT_LIMIT_ACTIVE_PROCESS = 0x00000008
32
+ JOB_OBJECT_LIMIT_AFFINITY = 0x00000010
33
+ JOB_OBJECT_LIMIT_BREAKAWAY_OK = 0x00000800
34
+ JOB_OBJECT_LIMIT_DIE_ON_UNHANDLED_EXCEPTION = 0x00000400
35
+ JOB_OBJECT_LIMIT_JOB_MEMORY = 0x00000200
36
+ JOB_OBJECT_LIMIT_JOB_TIME = 0x00000004
37
+ JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE = 0x00002000
38
+ JOB_OBJECT_LIMIT_PRESERVE_JOB_TIME = 0x00000040
39
+ JOB_OBJECT_LIMIT_PRIORITY_CLASS = 0x00000020
40
+ JOB_OBJECT_LIMIT_PROCESS_MEMORY = 0x00000100
41
+ JOB_OBJECT_LIMIT_PROCESS_TIME = 0x00000002
42
+ JOB_OBJECT_LIMIT_SCHEDULING_CLASS = 0x00000080
43
+ JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK = 0x00001000
44
+ JOB_OBJECT_LIMIT_SUBSET_AFFINITY = 0x00004000
45
+ JOB_OBJECT_LIMIT_WORKINGSET = 0x00000001
46
+
47
+ JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO = 4
48
+
49
+ WAIT_ABANDONED = 0x00000080
50
+ WAIT_IO_COMPLETION = 0x000000C0
51
+ WAIT_OBJECT_0 = 0x00000000
52
+ WAIT_TIMEOUT = 0x00000102
53
+ WAIT_FAILED = 0xFFFFFFFF
54
+
55
+ public
56
+
57
+ ABOVE_NORMAL_PRIORITY_CLASS = 0x00008000
58
+ BELOW_NORMAL_PRIORITY_CLASS = 0x00004000
59
+ HIGH_PRIORITY_CLASS = 0x00000080
60
+ IDLE_PRIORITY_CLASS = 0x00000040
61
+ NORMAL_PRIORITY_CLASS = 0x00000020
62
+ REALTIME_PRIORITY_CLASS = 0x00000100
63
+ end
64
+ end
@@ -1,26 +1,26 @@
1
- require 'ffi'
2
-
3
- module FFI
4
- extend FFI::Library
5
-
6
- ffi_lib :kernel32
7
-
8
- attach_function :FormatMessage, :FormatMessageA,
9
- [:ulong, :pointer, :ulong, :ulong, :pointer, :ulong, :pointer], :ulong
10
-
11
- def win_error(function, err=FFI.errno)
12
- flags = 0x00001000 | 0x00000200
13
- buf = FFI::MemoryPointer.new(:char, 1024)
14
-
15
- FormatMessage(flags, nil, err , 0x0409, buf, 1024, nil)
16
-
17
- function + ': ' + buf.read_string.strip
18
- end
19
-
20
- def raise_windows_error(function, err=FFI.errno)
21
- raise SystemCallError.new(win_error(function, err), err)
22
- end
23
-
24
- module_function :win_error
25
- module_function :raise_windows_error
26
- end
1
+ require 'ffi'
2
+
3
+ module FFI
4
+ extend FFI::Library
5
+
6
+ ffi_lib :kernel32
7
+
8
+ attach_function :FormatMessage, :FormatMessageA,
9
+ [:ulong, :pointer, :ulong, :ulong, :pointer, :ulong, :pointer], :ulong
10
+
11
+ def win_error(function, err=FFI.errno)
12
+ flags = 0x00001000 | 0x00000200
13
+ buf = FFI::MemoryPointer.new(:char, 1024)
14
+
15
+ FormatMessage(flags, nil, err , 0x0409, buf, 1024, nil)
16
+
17
+ function + ': ' + buf.read_string.strip
18
+ end
19
+
20
+ def raise_windows_error(function, err=FFI.errno)
21
+ raise SystemCallError.new(win_error(function, err), err)
22
+ end
23
+
24
+ module_function :win_error
25
+ module_function :raise_windows_error
26
+ end
@@ -1,175 +1,175 @@
1
- require 'ffi'
2
-
3
- module Windows
4
- module Structs
5
- extend FFI::Library
6
-
7
- typedef :uintptr_t, :handle
8
- typedef :ulong, :dword
9
- typedef :ushort, :word
10
-
11
- class LARGE_INTEGER < FFI::Union
12
- layout(:QuadPart, :long_long)
13
- end
14
-
15
- class JOBOBJECT_BASIC_PROCESS_ID_LIST < FFI::Struct
16
- layout(
17
- :NumberOfAssignedProcesses, :ulong,
18
- :NumberOfProcessIdsInList, :ulong,
19
- :ProcessIdList, [:uintptr_t, 100] # Limit 100 processes per job (?)
20
- )
21
- end
22
-
23
- class JOBOBJECT_BASIC_ACCOUNTING_INFORMATION < FFI::Struct
24
- layout(
25
- :TotalUserTime, LARGE_INTEGER,
26
- :TotalKernelTime, LARGE_INTEGER,
27
- :ThisPeriodTotalUserTime, LARGE_INTEGER,
28
- :ThisPeriodTotalKernelTime, LARGE_INTEGER,
29
- :TotalPageFaultCount, :ulong,
30
- :TotalProcesses, :ulong,
31
- :ActiveProcesses, :ulong,
32
- :TotalTerminatedProcesses, :ulong
33
- )
34
- end
35
-
36
- class IO_COUNTERS < FFI::Struct
37
- layout(
38
- :ReadOperationCount, :ulong_long,
39
- :WriteOperationCount, :ulong_long,
40
- :OtherOperationCount, :ulong_long,
41
- :ReadTransferCount, :ulong_long,
42
- :WriteTransferCount, :ulong_long,
43
- :OtherTransferCount, :ulong_long
44
- )
45
- end
46
-
47
- class JOBOBJECT_BASIC_AND_IO_ACCOUNTING_INFORMATION < FFI::Struct
48
- layout(
49
- :BasicInfo, JOBOBJECT_BASIC_ACCOUNTING_INFORMATION,
50
- :IoInfo, IO_COUNTERS
51
- )
52
- end
53
-
54
- class JOBOBJECT_BASIC_LIMIT_INFORMATION < FFI::Struct
55
- layout(
56
- :PerProcessUserTimeLimit, LARGE_INTEGER,
57
- :PerJobUserTimeLimit, LARGE_INTEGER,
58
- :LimitFlags, :ulong,
59
- :MinimumWorkingSetSize, :size_t,
60
- :MaximumWorkingSetSize, :size_t,
61
- :ActiveProcessLimit, :ulong,
62
- :Affinity, :uintptr_t,
63
- :PriorityClass, :ulong,
64
- :SchedulingClass, :ulong
65
- )
66
- end
67
-
68
- class JOBOBJECT_EXTENDED_LIMIT_INFORMATION < FFI::Struct
69
- layout(
70
- :BasicLimitInformation, JOBOBJECT_BASIC_LIMIT_INFORMATION,
71
- :IoInfo, IO_COUNTERS,
72
- :ProcessMemoryLimit, :size_t,
73
- :JobMemoryLimit, :size_t,
74
- :PeakProcessMemoryUsed, :size_t,
75
- :PeakJobMemoryUsed, :size_t
76
- )
77
- end
78
-
79
- class JOBOBJECT_ASSOCIATE_COMPLETION_PORT < FFI::Struct
80
- layout(:CompletionKey, :uintptr_t, :CompletionPort, :handle)
81
- end
82
-
83
- class SECURITY_ATTRIBUTES < FFI::Struct
84
- layout(
85
- :nLength, :ulong,
86
- :lpSecurityDescriptor, :pointer,
87
- :bInheritHandle, :bool
88
- )
89
- end
90
-
91
- class PROCESS_INFORMATION < FFI::Struct
92
- layout(
93
- :hProcess, :handle,
94
- :hThread, :handle,
95
- :dwProcessId, :dword,
96
- :dwThreadId, :dword
97
- )
98
- end
99
-
100
- class STARTUPINFO < FFI::Struct
101
- layout(
102
- :cb, :ulong,
103
- :lpReserved, :string,
104
- :lpDesktop, :string,
105
- :lpTitle, :string,
106
- :dwX, :dword,
107
- :dwY, :dword,
108
- :dwXSize, :dword,
109
- :dwYSize, :dword,
110
- :dwXCountChars, :dword,
111
- :dwYCountChars, :dword,
112
- :dwFillAttribute, :dword,
113
- :dwFlags, :dword,
114
- :wShowWindow, :word,
115
- :cbReserved2, :word,
116
- :lpReserved2, :pointer,
117
- :hStdInput, :handle,
118
- :hStdOutput, :handle,
119
- :hStdError, :handle
120
- )
121
- end
122
-
123
- # I'm assuming the anonymous struct for the internal union here.
124
- class Overlapped < FFI::Struct
125
- layout(
126
- :Internal, :ulong,
127
- :InternalHigh, :ulong,
128
- :Offset, :ulong,
129
- :OffsetHigh, :ulong,
130
- :hEvent, :ulong
131
- )
132
- end
133
-
134
- # Ruby Structs
135
-
136
- AccountInfo = Struct.new('AccountInfo',
137
- :total_user_time,
138
- :total_kernel_time,
139
- :this_period_total_user_time,
140
- :this_period_total_kernel_time,
141
- :total_page_fault_count,
142
- :total_processes,
143
- :active_processes,
144
- :total_terminated_processes,
145
- :read_operation_count,
146
- :write_operation_count,
147
- :other_operation_count,
148
- :read_transfer_count,
149
- :write_transfer_count,
150
- :other_transfer_count
151
- )
152
-
153
- LimitInfo = Struct.new('LimitInfo',
154
- :per_process_user_time_limit,
155
- :per_job_user_time_limit,
156
- :limit_flags,
157
- :minimum_working_set_size,
158
- :maximum_working_set_size,
159
- :active_process_limit,
160
- :affinity,
161
- :priority_class,
162
- :scheduling_class,
163
- :read_operation_count,
164
- :write_operation_count,
165
- :other_operation_count,
166
- :read_transfer_count,
167
- :write_transfer_count,
168
- :other_transfer_count,
169
- :process_memory_limit,
170
- :job_memory_limit,
171
- :peak_process_memory_used,
172
- :peek_job_memory_used
173
- )
174
- end
175
- end
1
+ require 'ffi'
2
+
3
+ module Windows
4
+ module Structs
5
+ extend FFI::Library
6
+
7
+ typedef :uintptr_t, :handle
8
+ typedef :ulong, :dword
9
+ typedef :ushort, :word
10
+
11
+ class LARGE_INTEGER < FFI::Union
12
+ layout(:QuadPart, :long_long)
13
+ end
14
+
15
+ class JOBOBJECT_BASIC_PROCESS_ID_LIST < FFI::Struct
16
+ layout(
17
+ :NumberOfAssignedProcesses, :ulong,
18
+ :NumberOfProcessIdsInList, :ulong,
19
+ :ProcessIdList, [:uintptr_t, 100] # Limit 100 processes per job (?)
20
+ )
21
+ end
22
+
23
+ class JOBOBJECT_BASIC_ACCOUNTING_INFORMATION < FFI::Struct
24
+ layout(
25
+ :TotalUserTime, LARGE_INTEGER,
26
+ :TotalKernelTime, LARGE_INTEGER,
27
+ :ThisPeriodTotalUserTime, LARGE_INTEGER,
28
+ :ThisPeriodTotalKernelTime, LARGE_INTEGER,
29
+ :TotalPageFaultCount, :ulong,
30
+ :TotalProcesses, :ulong,
31
+ :ActiveProcesses, :ulong,
32
+ :TotalTerminatedProcesses, :ulong
33
+ )
34
+ end
35
+
36
+ class IO_COUNTERS < FFI::Struct
37
+ layout(
38
+ :ReadOperationCount, :ulong_long,
39
+ :WriteOperationCount, :ulong_long,
40
+ :OtherOperationCount, :ulong_long,
41
+ :ReadTransferCount, :ulong_long,
42
+ :WriteTransferCount, :ulong_long,
43
+ :OtherTransferCount, :ulong_long
44
+ )
45
+ end
46
+
47
+ class JOBOBJECT_BASIC_AND_IO_ACCOUNTING_INFORMATION < FFI::Struct
48
+ layout(
49
+ :BasicInfo, JOBOBJECT_BASIC_ACCOUNTING_INFORMATION,
50
+ :IoInfo, IO_COUNTERS
51
+ )
52
+ end
53
+
54
+ class JOBOBJECT_BASIC_LIMIT_INFORMATION < FFI::Struct
55
+ layout(
56
+ :PerProcessUserTimeLimit, LARGE_INTEGER,
57
+ :PerJobUserTimeLimit, LARGE_INTEGER,
58
+ :LimitFlags, :ulong,
59
+ :MinimumWorkingSetSize, :size_t,
60
+ :MaximumWorkingSetSize, :size_t,
61
+ :ActiveProcessLimit, :ulong,
62
+ :Affinity, :uintptr_t,
63
+ :PriorityClass, :ulong,
64
+ :SchedulingClass, :ulong
65
+ )
66
+ end
67
+
68
+ class JOBOBJECT_EXTENDED_LIMIT_INFORMATION < FFI::Struct
69
+ layout(
70
+ :BasicLimitInformation, JOBOBJECT_BASIC_LIMIT_INFORMATION,
71
+ :IoInfo, IO_COUNTERS,
72
+ :ProcessMemoryLimit, :size_t,
73
+ :JobMemoryLimit, :size_t,
74
+ :PeakProcessMemoryUsed, :size_t,
75
+ :PeakJobMemoryUsed, :size_t
76
+ )
77
+ end
78
+
79
+ class JOBOBJECT_ASSOCIATE_COMPLETION_PORT < FFI::Struct
80
+ layout(:CompletionKey, :uintptr_t, :CompletionPort, :handle)
81
+ end
82
+
83
+ class SECURITY_ATTRIBUTES < FFI::Struct
84
+ layout(
85
+ :nLength, :ulong,
86
+ :lpSecurityDescriptor, :pointer,
87
+ :bInheritHandle, :bool
88
+ )
89
+ end
90
+
91
+ class PROCESS_INFORMATION < FFI::Struct
92
+ layout(
93
+ :hProcess, :handle,
94
+ :hThread, :handle,
95
+ :dwProcessId, :dword,
96
+ :dwThreadId, :dword
97
+ )
98
+ end
99
+
100
+ class STARTUPINFO < FFI::Struct
101
+ layout(
102
+ :cb, :ulong,
103
+ :lpReserved, :string,
104
+ :lpDesktop, :string,
105
+ :lpTitle, :string,
106
+ :dwX, :dword,
107
+ :dwY, :dword,
108
+ :dwXSize, :dword,
109
+ :dwYSize, :dword,
110
+ :dwXCountChars, :dword,
111
+ :dwYCountChars, :dword,
112
+ :dwFillAttribute, :dword,
113
+ :dwFlags, :dword,
114
+ :wShowWindow, :word,
115
+ :cbReserved2, :word,
116
+ :lpReserved2, :pointer,
117
+ :hStdInput, :handle,
118
+ :hStdOutput, :handle,
119
+ :hStdError, :handle
120
+ )
121
+ end
122
+
123
+ # I'm assuming the anonymous struct for the internal union here.
124
+ class Overlapped < FFI::Struct
125
+ layout(
126
+ :Internal, :ulong,
127
+ :InternalHigh, :ulong,
128
+ :Offset, :ulong,
129
+ :OffsetHigh, :ulong,
130
+ :hEvent, :ulong
131
+ )
132
+ end
133
+
134
+ # Ruby Structs
135
+
136
+ AccountInfo = Struct.new('AccountInfo',
137
+ :total_user_time,
138
+ :total_kernel_time,
139
+ :this_period_total_user_time,
140
+ :this_period_total_kernel_time,
141
+ :total_page_fault_count,
142
+ :total_processes,
143
+ :active_processes,
144
+ :total_terminated_processes,
145
+ :read_operation_count,
146
+ :write_operation_count,
147
+ :other_operation_count,
148
+ :read_transfer_count,
149
+ :write_transfer_count,
150
+ :other_transfer_count
151
+ )
152
+
153
+ LimitInfo = Struct.new('LimitInfo',
154
+ :per_process_user_time_limit,
155
+ :per_job_user_time_limit,
156
+ :limit_flags,
157
+ :minimum_working_set_size,
158
+ :maximum_working_set_size,
159
+ :active_process_limit,
160
+ :affinity,
161
+ :priority_class,
162
+ :scheduling_class,
163
+ :read_operation_count,
164
+ :write_operation_count,
165
+ :other_operation_count,
166
+ :read_transfer_count,
167
+ :write_transfer_count,
168
+ :other_transfer_count,
169
+ :process_memory_limit,
170
+ :job_memory_limit,
171
+ :peak_process_memory_used,
172
+ :peek_job_memory_used
173
+ )
174
+ end
175
+ end
@@ -1,126 +1,130 @@
1
- #######################################################################
2
- # test_win32_job.rb
3
- #
4
- # Test suite for the win32-job library. You should run these tests
5
- # via the "rake test" command.
6
- #######################################################################
7
- require 'test-unit'
8
- require 'win32/job'
9
-
10
- class TC_Win32_Job < Test::Unit::TestCase
11
- def setup
12
- @name = 'ruby_xxxxxx'
13
- @job = Win32::Job.new(@name)
14
- @pid = Process.spawn('notepad')
15
- end
16
-
17
- test "version number is what we expect" do
18
- assert_equal('0.1.0', Win32::Job::VERSION)
19
- end
20
-
21
- test "constructor argument may be omitted" do
22
- assert_nothing_raised{ Win32::Job.new }
23
- end
24
-
25
- test "constructor accepts a name for the job" do
26
- assert_nothing_raised{ Win32::Job.new(@name) }
27
- end
28
-
29
- test "argument to constructor must be a string" do
30
- assert_raise(TypeError){ Win32::Job.new(1) }
31
- end
32
-
33
- test "job_name basic functionality" do
34
- assert_respond_to(@job, :job_name)
35
- assert_nothing_raised{ @job.job_name }
36
- assert_kind_of(String, @job.job_name)
37
- end
38
-
39
- test "job_name is read-only" do
40
- assert_raise(NoMethodError){ @job.job_name = 'foo' }
41
- end
42
-
43
- test "name is an alias for job_name" do
44
- assert_alias_method(@job, :name, :job_name)
45
- end
46
-
47
- test "close basic functionality" do
48
- assert_respond_to(@job, :close)
49
- assert_nothing_raised{ @job.close }
50
- end
51
-
52
- test "calling close multiple times has no effect" do
53
- assert_nothing_raised{ @job.close }
54
- assert_nothing_raised{ @job.close }
55
- assert_nothing_raised{ @job.close }
56
- end
57
-
58
- test "close method does not accept any arguments" do
59
- assert_raise(ArgumentError){ @job.close(1) }
60
- end
61
-
62
- test "add_process basic functionality" do
63
- assert_respond_to(@job, :add_process)
64
- end
65
-
66
- test "add_process works as expected" do
67
- assert_nothing_raised{ @job.add_process(@pid) }
68
- end
69
-
70
- test "add process requires a single argument" do
71
- assert_raise(ArgumentError){ @job.add_process }
72
- end
73
-
74
- test "kill basic functionality" do
75
- assert_respond_to(@job, :kill)
76
- end
77
-
78
- test "kill works as expected" do
79
- @job.add_process(@pid)
80
- assert_equal(@pid, @job.process_list.first)
81
- assert_nothing_raised{ @job.kill }
82
- assert_true(@job.process_list.empty?)
83
- end
84
-
85
- test "terminate is an alias for kill" do
86
- assert_alias_method(@job, :kill, :terminate)
87
- end
88
-
89
- test "configure_limit basic functionality" do
90
- assert_nothing_raised{ @job.configure_limit }
91
- end
92
-
93
- test "configure_limit works as expected" do
94
- assert_nothing_raised{
95
- @job.configure_limit(
96
- :breakaway_ok => true,
97
- :kill_on_job_close => true,
98
- :process_memory => 1024 * 8,
99
- :process_time => 1000
100
- )
101
- }
102
- end
103
-
104
- test "configure_limit raises an error if it detects an invalid option" do
105
- assert_raise(ArgumentError){ @job.configure_limit(:bogus => 1) }
106
- end
107
-
108
- test "priority constants are defined" do
109
- assert_not_nil(Win32::Job::ABOVE_NORMAL_PRIORITY_CLASS)
110
- assert_not_nil(Win32::Job::BELOW_NORMAL_PRIORITY_CLASS)
111
- assert_not_nil(Win32::Job::HIGH_PRIORITY_CLASS)
112
- assert_not_nil(Win32::Job::IDLE_PRIORITY_CLASS)
113
- assert_not_nil(Win32::Job::NORMAL_PRIORITY_CLASS)
114
- assert_not_nil(Win32::Job::REALTIME_PRIORITY_CLASS)
115
- end
116
-
117
- def teardown
118
- @name = nil
119
-
120
- @job.close
121
- @job = nil
122
-
123
- Process.kill(9, @pid) rescue nil
124
- @pid = nil
125
- end
126
- end
1
+ #######################################################################
2
+ # test_win32_job.rb
3
+ #
4
+ # Test suite for the win32-job library. You should run these tests
5
+ # via the "rake test" command.
6
+ #######################################################################
7
+ require 'test-unit'
8
+ require 'win32/job'
9
+
10
+ class TC_Win32_Job < Test::Unit::TestCase
11
+ def setup
12
+ @name = 'ruby_xxxxxx'
13
+ @job = Win32::Job.new(@name)
14
+ @pid = Process.spawn('notepad')
15
+ end
16
+
17
+ test "version number is what we expect" do
18
+ assert_equal('0.1.1', Win32::Job::VERSION)
19
+ end
20
+
21
+ test "constructor argument may be omitted" do
22
+ assert_nothing_raised{ Win32::Job.new }
23
+ end
24
+
25
+ test "constructor accepts a name for the job" do
26
+ assert_nothing_raised{ Win32::Job.new(@name) }
27
+ end
28
+
29
+ test "argument to constructor must be a string" do
30
+ assert_raise(TypeError){ Win32::Job.new(1) }
31
+ end
32
+
33
+ test "job_name basic functionality" do
34
+ assert_respond_to(@job, :job_name)
35
+ assert_nothing_raised{ @job.job_name }
36
+ assert_kind_of(String, @job.job_name)
37
+ end
38
+
39
+ test "job_name is read-only" do
40
+ assert_raise(NoMethodError){ @job.job_name = 'foo' }
41
+ end
42
+
43
+ test "name is an alias for job_name" do
44
+ assert_alias_method(@job, :name, :job_name)
45
+ end
46
+
47
+ test "close basic functionality" do
48
+ assert_respond_to(@job, :close)
49
+ assert_nothing_raised{ @job.close }
50
+ end
51
+
52
+ test "calling close multiple times has no effect" do
53
+ assert_nothing_raised{ @job.close }
54
+ assert_nothing_raised{ @job.close }
55
+ assert_nothing_raised{ @job.close }
56
+ end
57
+
58
+ test "close method does not accept any arguments" do
59
+ assert_raise(ArgumentError){ @job.close(1) }
60
+ end
61
+
62
+ test "add_process basic functionality" do
63
+ assert_respond_to(@job, :add_process)
64
+ end
65
+
66
+ test "add_process works as expected" do
67
+ assert_nothing_raised{ @job.add_process(@pid) }
68
+ end
69
+
70
+ test "add process requires a single argument" do
71
+ assert_raise(ArgumentError){ @job.add_process }
72
+ end
73
+
74
+ test "kill basic functionality" do
75
+ assert_respond_to(@job, :kill)
76
+ end
77
+
78
+ test "kill works as expected" do
79
+ @job.add_process(@pid)
80
+ assert_equal(@pid, @job.process_list.first)
81
+ assert_nothing_raised{ @job.kill }
82
+ assert_true(@job.process_list.empty?)
83
+ end
84
+
85
+ test "terminate is an alias for kill" do
86
+ assert_alias_method(@job, :kill, :terminate)
87
+ end
88
+
89
+ test "configure_limit basic functionality" do
90
+ assert_nothing_raised{ @job.configure_limit }
91
+ end
92
+
93
+ test "configure_limit works as expected" do
94
+ assert_nothing_raised{
95
+ @job.configure_limit(
96
+ :breakaway_ok => true,
97
+ :kill_on_job_close => true,
98
+ :process_memory => 1024 * 8,
99
+ :process_time => 1000
100
+ )
101
+ }
102
+ end
103
+
104
+ test "configure_limit raises an error if it detects an invalid option" do
105
+ assert_raise(ArgumentError){ @job.configure_limit(:bogus => 1) }
106
+ end
107
+
108
+ test "wait method basic functionality" do
109
+ assert_respond_to(@job, :wait)
110
+ end
111
+
112
+ test "priority constants are defined" do
113
+ assert_not_nil(Win32::Job::ABOVE_NORMAL_PRIORITY_CLASS)
114
+ assert_not_nil(Win32::Job::BELOW_NORMAL_PRIORITY_CLASS)
115
+ assert_not_nil(Win32::Job::HIGH_PRIORITY_CLASS)
116
+ assert_not_nil(Win32::Job::IDLE_PRIORITY_CLASS)
117
+ assert_not_nil(Win32::Job::NORMAL_PRIORITY_CLASS)
118
+ assert_not_nil(Win32::Job::REALTIME_PRIORITY_CLASS)
119
+ end
120
+
121
+ def teardown
122
+ @name = nil
123
+
124
+ @job.close
125
+ @job = nil
126
+
127
+ Process.kill(9, @pid) rescue nil
128
+ @pid = nil
129
+ end
130
+ end
data/win32-job.gemspec CHANGED
@@ -1,28 +1,28 @@
1
- require 'rubygems'
2
-
3
- Gem::Specification.new do |spec|
4
- spec.name = 'win32-job'
5
- spec.version = '0.1.0'
6
- spec.author = 'Daniel J. Berger'
7
- spec.license = 'Artistic 2.0'
8
- spec.email = 'djberg96@gmail.com'
9
- spec.homepage = 'http://github.com/djberg96/win32-job'
10
- spec.summary = 'Interface for Windows jobs (process groups)'
11
- spec.test_file = 'test/test_win32_job.rb'
12
- spec.files = Dir['**/*'].reject{ |f| f.include?('git') }
13
-
14
- spec.extra_rdoc_files = ['README', 'CHANGES', 'MANIFEST']
15
- spec.rubyforge_project = 'win32utils'
16
- spec.required_ruby_version = '> 1.9.1'
17
-
18
- spec.add_dependency('ffi')
19
-
20
- spec.add_development_dependency('rake')
21
- spec.add_development_dependency('test-unit')
22
-
23
- spec.description = <<-EOF
24
- The win32-job library provides an interface for jobs (process groups)
25
- on MS Windows. This allows you to apply various limits and behavior to
26
- groups of processes on Windows.
27
- EOF
28
- end
1
+ require 'rubygems'
2
+
3
+ Gem::Specification.new do |spec|
4
+ spec.name = 'win32-job'
5
+ spec.version = '0.1.1'
6
+ spec.authors = ['Daniel J. Berger', 'Park Heesob']
7
+ spec.license = 'Artistic 2.0'
8
+ spec.email = 'djberg96@gmail.com'
9
+ spec.homepage = 'http://github.com/djberg96/win32-job'
10
+ spec.summary = 'Interface for Windows jobs (process groups)'
11
+ spec.test_file = 'test/test_win32_job.rb'
12
+ spec.files = Dir['**/*'].reject{ |f| f.include?('git') }
13
+
14
+ spec.extra_rdoc_files = ['README', 'CHANGES', 'MANIFEST']
15
+ spec.rubyforge_project = 'win32utils'
16
+ spec.required_ruby_version = '> 1.9.3'
17
+
18
+ spec.add_dependency('ffi')
19
+
20
+ spec.add_development_dependency('rake')
21
+ spec.add_development_dependency('test-unit')
22
+
23
+ spec.description = <<-EOF
24
+ The win32-job library provides an interface for jobs (process groups)
25
+ on MS Windows. This allows you to apply various limits and behavior to
26
+ groups of processes on Windows.
27
+ EOF
28
+ end
metadata CHANGED
@@ -1,14 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: win32-job
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Daniel J. Berger
8
+ - Park Heesob
8
9
  autorequire:
9
10
  bindir: bin
10
11
  cert_chain: []
11
- date: 2014-02-04 00:00:00.000000000 Z
12
+ date: 2014-02-05 00:00:00.000000000 Z
12
13
  dependencies:
13
14
  - !ruby/object:Gem::Dependency
14
15
  name: ffi
@@ -65,17 +66,16 @@ extra_rdoc_files:
65
66
  - MANIFEST
66
67
  files:
67
68
  - CHANGES
69
+ - MANIFEST
70
+ - README
71
+ - Rakefile
68
72
  - examples/example_job.rb
73
+ - lib/win32/job.rb
69
74
  - lib/win32/job/constants.rb
70
75
  - lib/win32/job/functions.rb
71
76
  - lib/win32/job/helper.rb
72
77
  - lib/win32/job/structs.rb
73
- - lib/win32/job.rb
74
- - MANIFEST
75
- - Rakefile
76
- - README
77
78
  - test/test_win32_job.rb
78
- - win32-job-0.1.0.gem
79
79
  - win32-job.gemspec
80
80
  homepage: http://github.com/djberg96/win32-job
81
81
  licenses:
@@ -89,7 +89,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
89
89
  requirements:
90
90
  - - '>'
91
91
  - !ruby/object:Gem::Version
92
- version: 1.9.1
92
+ version: 1.9.3
93
93
  required_rubygems_version: !ruby/object:Gem::Requirement
94
94
  requirements:
95
95
  - - '>='
@@ -97,7 +97,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
97
97
  version: '0'
98
98
  requirements: []
99
99
  rubyforge_project: win32utils
100
- rubygems_version: 2.0.3
100
+ rubygems_version: 2.2.1
101
101
  signing_key:
102
102
  specification_version: 4
103
103
  summary: Interface for Windows jobs (process groups)
data/win32-job-0.1.0.gem DELETED
Binary file