win32-job 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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