win32-ipc 0.5.3 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGES CHANGED
@@ -1,3 +1,11 @@
1
+ == 0.6.0 - 8-Jul-2012
2
+ * Now uses FFI instead of win32-api.
3
+ * The #signaled? method now has a meaningful default implementation.
4
+ * If an internal C function fails it raises a SystemCallError (Errno) instead
5
+ of an Ipc::Error.
6
+ * The Ipc::Error class, although defined for those who wish to still use it
7
+ in inherited classes, is no longer used internally.
8
+
1
9
  == 0.5.3 - 19-Apr-2010
2
10
  * Added a few gem related tasks to the Rakefile, and removed an old install
3
11
  task.
data/README CHANGED
@@ -6,7 +6,7 @@ gem install win32-ipc
6
6
 
7
7
  = Synopsis
8
8
  There is no synopsis. Don't use this module directly. It is used as the basis
9
- for other libraries, such as win32-mutex, etc.
9
+ for other libraries, such as win32-mutex, etc. Think of it as an interface.
10
10
 
11
11
  = Notes
12
12
  Originally based on the Win32::Ipc Perl module by Gurusamy Sarathy.
@@ -14,14 +14,14 @@ Originally based on the Win32::Ipc Perl module by Gurusamy Sarathy.
14
14
  This library is a prerequisite for several other IPC related Windows packages.
15
15
 
16
16
  = Known Bugs
17
- None that I know of. Please log any other bug reports on the RubyForge
18
- project page at http://www.rubyforge.net/projects/win32utils
17
+ None that I know of. Please log any other bug reports on the project page
18
+ at https://github.com/djberg96/win32-ipc.
19
19
 
20
20
  = License
21
21
  Artistic 2.0
22
22
 
23
23
  = Copyright
24
- (C) 2003-2010 Daniel J. Berger, All Rights Reserved
24
+ (C) 2003-2012 Daniel J. Berger, All Rights Reserved
25
25
 
26
26
  = Warranty
27
27
  This package is provided "as is" and without any express or
data/Rakefile CHANGED
@@ -1,14 +1,10 @@
1
1
  require 'rake'
2
2
  require 'rake/testtask'
3
- require 'rbconfig'
4
- include Config
3
+ require 'rake/clean'
5
4
 
6
- namespace 'gem' do
7
- desc 'Delete any .gem files in the current directory'
8
- task :clean do
9
- Dir['*.gem'].each{ |f| File.delete(f) }
10
- end
5
+ CLEAN.include("**/*.gem", "**/*.rbc")
11
6
 
7
+ namespace 'gem' do
12
8
  desc 'Create the win32-ipc gem'
13
9
  task :create => [:clean] do
14
10
  spec = eval(IO.read('win32-ipc.gemspec'))
@@ -23,6 +19,8 @@ namespace 'gem' do
23
19
  end
24
20
 
25
21
  Rake::TestTask.new do |t|
26
- t.verbose = true
27
- t.warning = true
22
+ t.verbose = true
23
+ t.warning = true
28
24
  end
25
+
26
+ task :default => :test
@@ -1,159 +1,182 @@
1
- require 'windows/error'
2
- require 'windows/synchronize'
3
- require 'windows/handle'
1
+ require 'ffi'
4
2
 
5
3
  # The Win32 module serves as a namespace only.
6
4
  module Win32
7
5
 
8
- # This is a an abstract base class for IPC related classes, such as
9
- # Events and Semaphores.
10
- #
11
- class Ipc
12
- include Windows::Error
13
- include Windows::Synchronize
14
- include Windows::Handle
15
-
16
- class Error < StandardError; end
17
-
18
- # The version of the win32-ipc library
19
- VERSION = '0.5.3'
20
-
21
- SIGNALED = 1
22
- ABANDONED = -1
23
- TIMEOUT = 0
24
-
25
- # The HANDLE object (an unsigned long value). Mostly provided for
26
- # subclasses to use internally when needed.
27
- #
28
- attr_reader :handle
29
-
30
- # Creates and returns a new IPC object. Since the IPC class is meant
31
- # as an abstract base class, you should never call this method directly.
32
- #
33
- def initialize(handle)
34
- @handle = handle
35
- @signaled = false
6
+ # This is a an abstract base class for IPC related classes, such as
7
+ # Events and Semaphores.
8
+ #
9
+ class Ipc
10
+ extend FFI::Library
11
+ ffi_lib :kernel32
12
+
13
+ attach_function :CloseHandle, [:ulong], :bool
14
+ attach_function :WaitForSingleObject, [:ulong, :ulong], :ulong
15
+ attach_function :WaitForMultipleObjects, [:ulong, :pointer, :bool, :ulong], :ulong
16
+
17
+ private_class_method :CloseHandle, :WaitForSingleObject, :WaitForMultipleObjects
18
+
19
+ # The version of the win32-ipc library
20
+ VERSION = '0.6.0'
21
+
22
+ SIGNALED = 1
23
+ ABANDONED = -1
24
+ TIMEOUT = 0
25
+ INFINITE = 0xFFFFFFFF
26
+
27
+ WAIT_OBJECT_0 = 0
28
+ WAIT_TIMEOUT = 0x102
29
+ WAIT_ABANDONED = 128
30
+ WAIT_ABANDONED_0 = WAIT_ABANDONED
31
+ WAIT_FAILED = 0xFFFFFFFF
32
+
33
+ # The HANDLE object (an unsigned long value). Mostly provided for
34
+ # subclasses to use internally when needed.
35
+ #
36
+ attr_reader :handle
37
+
38
+ # Creates and returns a new IPC object. Since the IPC class is meant
39
+ # as an abstract base class, you should never call this method directly.
40
+ #
41
+ def initialize(handle)
42
+ @handle = handle
43
+ @signaled = false
44
+ end
45
+
46
+ # Closes the handle object provided in the constructor.
47
+ #
48
+ def close
49
+ CloseHandle(@handle)
50
+ end
51
+
52
+ # Returns whether or not the IPC object is in a signaled state.
53
+ #--
54
+ # This method assumes a single object. You may need to redefine this
55
+ # to suit your needs in your subclass.
56
+ #
57
+ def signaled?
58
+ state = WaitForSingleObject(@handle, 0)
59
+
60
+ if state == WAIT_FAILED
61
+ raise SystemCallError.new("WaitForSingleObject", FFI.errno)
62
+ elsif state == WAIT_OBJECT_0
63
+ @signaled = true
64
+ else
65
+ @signaled = false
36
66
  end
37
-
38
- # Closes the handle object provided in the constructor.
39
- #
40
- def close
41
- CloseHandle(@handle)
67
+
68
+ @signaled
69
+ end
70
+
71
+ # call-seq:
72
+ # Ipc#wait(timeout)
73
+ # Ipc#wait(timeout){ block called when signaled }
74
+ #
75
+ # Waits for the calling object to be signaled. The +timeout+ value is
76
+ # the maximum time to wait, in seconds. A timeout of 0 returns immediately.
77
+ #
78
+ # Returns SIGNALED (1), ABANDONED (-1) or TIMEOUT (0). Raises a
79
+ # SystemCallError (Errno) if the wait fails for some reason.
80
+ #
81
+ def wait(timeout = INFINITE)
82
+ timeout *= 1000 if timeout && timeout != INFINITE
83
+
84
+ wait = WaitForSingleObject(@handle, timeout)
85
+
86
+ case wait
87
+ when WAIT_FAILED
88
+ raise SystemCallError.new("WaitForSingleObject", FFI.errno)
89
+ when WAIT_OBJECT_0
90
+ @signaled = true
91
+ yield if block_given?
92
+ return SIGNALED
93
+ when WAIT_ABANDONED
94
+ return ABANDONED
95
+ when WAIT_TIMEOUT
96
+ return TIMEOUT
97
+ else
98
+ raise SystemCallError.new("WaitForSingleObject", FFI.errno)
42
99
  end
100
+ end
43
101
 
44
- # Returns whether or not the IPC object is in a signaled state.
45
- #
46
- def signaled?
47
- @signaled
102
+ # :call-seq:
103
+ # IPC#wait_any([ipc_objects], timeout = INFINITE)
104
+ #
105
+ # Waits for at least one of the +ipc_objects+ to be signaled. The
106
+ # +timeout+ value is maximum time to wait in seconds. A timeout of 0
107
+ # returns immediately.
108
+ #
109
+ # Returns the index+1 of the object that was signaled. If multiple
110
+ # objects are signaled, the one with the lowest index is returned.
111
+ # Returns 0 if no objects are signaled.
112
+ #
113
+ def wait_any(ipc_objects, timeout=INFINITE)
114
+ timeout *= 1000 if timeout && timeout != INFINITE
115
+ wait_for_multiple(ipc_objects, 0, timeout)
116
+ end
117
+
118
+ # :call-seq:
119
+ # IPC#wait_all([ipc_objects], timeout = INFINITE)
120
+ #
121
+ # Identical to IPC#wait_any, except that it waits for all +ipc_objects+
122
+ # to be signaled instead of just one.
123
+ #
124
+ # Returns the index of the last object signaled. If at least one of the
125
+ # objects is an abandoned mutex, the return value is negative.
126
+ #
127
+ def wait_all(ipc_objects, timeout=INFINITE)
128
+ timeout *= 1000 if timeout && timeout != INFINITE
129
+ wait_for_multiple(ipc_objects, 1, timeout)
130
+ end
131
+
132
+ private
133
+
134
+ # Waits until one or all (depending on the value of +wait_all+) of the
135
+ # +ipc_objects+ are in the signaled state or the +timeout+ interval
136
+ # elapses.
137
+ #
138
+ def wait_for_multiple(ipc_objects, wait_all=0, timeout=INFINITE)
139
+ unless ipc_objects.is_a?(Array)
140
+ msg = 'invalid argument - must be an array of Ipc objects'
141
+ raise TypeError, msg
48
142
  end
49
-
50
- # call-seq:
51
- # Ipc#wait(timeout)
52
- # Ipc#wait(timeout){ block called when signaled }
53
- #
54
- # Waits for the calling object to be signaled. The +timeout+ value is
55
- # the maximum time to wait, in seconds. A timeout of 0 returns
56
- # immediately.
57
- #
58
- # Returns SIGNALED (1), ABANDONED (-1) or TIMEOUT (0). Raises an
59
- # IPC::Error if the wait fails for some reason.
60
- #
61
- def wait(timeout = INFINITE)
62
- timeout *= 1000 if timeout && timeout != INFINITE
63
-
64
- wait = WaitForSingleObject(@handle, timeout)
65
-
66
- case wait
67
- when WAIT_FAILED
68
- raise Error, get_last_error
69
- when WAIT_OBJECT_0
70
- @signaled = true
71
- yield if block_given?
72
- return SIGNALED
73
- when WAIT_ABANDONED
74
- return ABANDONED
75
- when WAIT_TIMEOUT
76
- return TIMEOUT
77
- else
78
- raise Error, get_last_error
79
- end
143
+
144
+ length = ipc_objects.length
145
+
146
+ if length == 0
147
+ raise ArgumentError, 'no objects to wait for'
80
148
  end
81
-
82
- # :call-seq:
83
- # IPC#wait_any([ipc_objects], timeout = INFINITE)
84
- #
85
- # Waits for at least one of the +ipc_objects+ to be signaled. The
86
- # +timeout+ value is maximum time to wait in seconds. A timeout of 0
87
- # returns immediately.
88
- #
89
- # Returns the index+1 of the object that was signaled. If multiple
90
- # objects are signaled, the one with the lowest index is returned.
91
- # Returns 0 if no objects are signaled.
92
- #
93
- def wait_any(ipc_objects, timeout=INFINITE)
94
- timeout *= 1000 if timeout && timeout != INFINITE
95
- wait_for_multiple(ipc_objects, 0, timeout)
149
+
150
+ ptr = FFI::MemoryPointer.new(:ulong, ipc_objects.size)
151
+
152
+ handles = ipc_objects.map{ |o| o.handle }
153
+ ptr.write_array_of_ulong(handles)
154
+
155
+ wait = WaitForMultipleObjects(
156
+ length,
157
+ ptr,
158
+ wait_all,
159
+ timeout
160
+ )
161
+
162
+ if wait == WAIT_FAILED
163
+ raise SystemCallError.new("WaitForMultipleObjects", FFI.errno)
96
164
  end
97
-
98
- # :call-seq:
99
- # IPC#wait_all([ipc_objects], timeout = INFINITE)
100
- #
101
- # Identical to IPC#wait_any, except that it waits for all +ipc_objects+
102
- # to be signaled instead of just one.
103
- #
104
- # Returns the index of the last object signaled. If at least one of the
105
- # objects is an abandoned mutex, the return value is negative.
106
- #
107
- def wait_all(ipc_objects, timeout=INFINITE)
108
- timeout *= 1000 if timeout && timeout != INFINITE
109
- wait_for_multiple(ipc_objects, 1, timeout)
165
+
166
+ # signaled
167
+ if (wait >= WAIT_OBJECT_0) && (wait < WAIT_OBJECT_0 + length)
168
+ return wait - WAIT_OBJECT_0 + 1
110
169
  end
111
-
112
- private
113
-
114
- # Waits until one or all (depending on the value of +wait_all+) of the
115
- # +ipc_objects+ are in the signaled state or the +timeout+ interval
116
- # elapses.
117
- #
118
- def wait_for_multiple(ipc_objects, wait_all=0, timeout=INFINITE)
119
- unless ipc_objects.is_a?(Array)
120
- msg = 'invalid argument - must be an array of Ipc objects'
121
- raise TypeError, msg
122
- end
123
-
124
- length = ipc_objects.length
125
-
126
- if length == 0
127
- raise Error, 'no objects to wait for'
128
- end
129
-
130
- handles = ipc_objects.map{ |o| o.handle }
131
-
132
- wait = WaitForMultipleObjects(
133
- length,
134
- handles.pack('L*'),
135
- wait_all,
136
- timeout
137
- )
138
-
139
- if wait == WAIT_FAILED
140
- raise Error, get_last_error
141
- end
142
-
143
- # signaled
144
- if (wait >= WAIT_OBJECT_0) && (wait < WAIT_OBJECT_0 + length)
145
- return wait - WAIT_OBJECT_0 + 1
146
- end
147
-
148
- # abandoned mutex - return negative value
149
- if (wait >= WAIT_ABANDONED) && (wait < WAIT_ABANDONED + length)
150
- return -wait - WAIT_ABANDONED + 1
151
- end
152
-
153
- # timed out
154
- return 0 if wait == WAIT_TIMEOUT
155
-
156
- nil
170
+
171
+ # abandoned mutex - return negative value
172
+ if (wait >= WAIT_ABANDONED) && (wait < WAIT_ABANDONED + length)
173
+ return -wait - WAIT_ABANDONED + 1
157
174
  end
158
- end
175
+
176
+ # timed out
177
+ return 0 if wait == WAIT_TIMEOUT
178
+
179
+ nil
180
+ end
181
+ end
159
182
  end
@@ -8,66 +8,80 @@
8
8
  # You should run this test via the 'rake test' task.
9
9
  ##########################################################################
10
10
  require 'win32/ipc'
11
- require 'test/unit'
11
+ require 'test-unit'
12
12
  include Win32
13
13
 
14
14
  class TC_Win32_Ipc < Test::Unit::TestCase
15
15
  def setup
16
16
  @ipc = Ipc.new(1)
17
17
  end
18
-
19
- def test_version
20
- assert_equal('0.5.3', Ipc::VERSION)
18
+
19
+ test "version is set to expected value" do
20
+ assert_equal('0.6.0', Ipc::VERSION)
21
21
  end
22
-
23
- def test_handle
22
+
23
+ test "handle method basic functionality" do
24
24
  assert_respond_to(@ipc, :handle)
25
25
  assert_equal(1, @ipc.handle)
26
26
  end
27
-
28
- def test_signaled
27
+
28
+ test "signaled? method is defined" do
29
29
  assert_respond_to(@ipc, :signaled?)
30
- assert_equal(false, @ipc.signaled?)
31
30
  end
32
-
33
- def test_wait
31
+
32
+ test "wait method is defined" do
34
33
  assert_respond_to(@ipc, :wait)
35
34
  end
36
-
37
- def test_wait_expected_errors
38
- assert_raises(Ipc::Error){ @ipc.wait }
35
+
36
+ test "wait raises ENXIO if handle is invalid" do
37
+ assert_raises(Errno::ENXIO){ @ipc.wait }
38
+ end
39
+
40
+ test "wait accepts a maximum of one argument" do
39
41
  assert_raises(ArgumentError){ @ipc.wait(1,2) }
40
42
  end
41
-
42
- def test_wait_any
43
+
44
+ test "wait_any method is defined" do
43
45
  assert_respond_to(@ipc, :wait_any)
44
46
  end
45
-
46
- def test_wait_any_expected_errors
47
- assert_raises(Ipc::Error){ @ipc.wait_any([]) }
47
+
48
+ test "wait_any raises an ArgumentError if the array is empty" do
49
+ assert_raises(ArgumentError){ @ipc.wait_any([]) }
50
+ end
51
+
52
+ test "wait_any only accepts an array" do
48
53
  assert_raises(TypeError){ @ipc.wait_any(1,2) }
49
54
  end
50
-
51
- def test_wait_all
55
+
56
+ test "wait_all method is defined" do
52
57
  assert_respond_to(@ipc, :wait_all)
53
58
  end
54
-
55
- def test_wait_all_expected_errors
56
- assert_raises(Ipc::Error){ @ipc.wait_all([]) }
59
+
60
+ test "wait_all raises an ArgumentError if the array is empty" do
61
+ assert_raises(ArgumentError){ @ipc.wait_all([]) }
62
+ end
63
+
64
+ test "wait_all only accepts an array" do
57
65
  assert_raises(TypeError){ @ipc.wait_all(1,2) }
58
66
  end
59
-
60
- def test_close
67
+
68
+ test "close method basic functionality" do
61
69
  assert_respond_to(@ipc, :close)
62
70
  assert_nothing_raised{ @ipc.close }
63
71
  end
64
-
65
- def test_constants
72
+
73
+ test "expected constants are defined" do
66
74
  assert_not_nil(Ipc::SIGNALED)
67
75
  assert_not_nil(Ipc::ABANDONED)
68
76
  assert_not_nil(Ipc::TIMEOUT)
69
77
  end
70
-
78
+
79
+ test "ffi functions are private" do
80
+ assert_not_respond_to(Ipc, :CloseHandle)
81
+ assert_not_respond_to(Ipc, :WaitForSingleObject)
82
+ assert_not_respond_to(Ipc, :WaitForMultipleObjects)
83
+ end
84
+
71
85
  def teardown
72
86
  @ipc = nil
73
87
  end
@@ -2,21 +2,20 @@ require 'rubygems'
2
2
 
3
3
  Gem::Specification.new do |spec|
4
4
  spec.name = 'win32-ipc'
5
- spec.version = '0.5.3'
5
+ spec.version = '0.6.0'
6
6
  spec.authors = ['Daniel J. Berger', 'Park Heesob']
7
7
  spec.license = 'Artistic 2.0'
8
8
  spec.email = 'djberg96@gmail.com'
9
9
  spec.homepage = 'http://www.rubyforge.org/projects/win32utils'
10
- spec.platform = Gem::Platform::RUBY
11
10
  spec.summary = 'An abstract base class for Windows synchronization objects.'
12
11
  spec.test_file = 'test/test_win32_ipc.rb'
13
- spec.has_rdoc = true
14
12
  spec.files = Dir['**/*'].reject{ |f| f.include?('git') }
15
13
 
16
14
  spec.extra_rdoc_files = ['README', 'CHANGES', 'MANIFEST']
17
15
  spec.rubyforge_project = 'win32utils'
18
16
 
19
- spec.add_dependency('windows-pr', '>= 1.0.6')
17
+ spec.add_dependency('ffi')
18
+ spec.add_development_dependency('test-unit')
20
19
 
21
20
  spec.description = <<-EOF
22
21
  The win32-ipc library provides the Win32::IPC class. This is meant to
metadata CHANGED
@@ -1,39 +1,60 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: win32-ipc
3
- version: !ruby/object:Gem::Version
4
- version: 0.5.3
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.6.0
5
+ prerelease:
5
6
  platform: ruby
6
- authors:
7
+ authors:
7
8
  - Daniel J. Berger
8
9
  - Park Heesob
9
10
  autorequire:
10
11
  bindir: bin
11
12
  cert_chain: []
12
-
13
- date: 2010-04-19 00:00:00 -06:00
14
- default_executable:
15
- dependencies:
16
- - !ruby/object:Gem::Dependency
17
- name: windows-pr
13
+ date: 2012-07-09 00:00:00.000000000 Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: ffi
17
+ requirement: !ruby/object:Gem::Requirement
18
+ none: false
19
+ requirements:
20
+ - - ! '>='
21
+ - !ruby/object:Gem::Version
22
+ version: '0'
18
23
  type: :runtime
19
- version_requirement:
20
- version_requirements: !ruby/object:Gem::Requirement
21
- requirements:
22
- - - ">="
23
- - !ruby/object:Gem::Version
24
- version: 1.0.6
25
- version:
26
- description: " The win32-ipc library provides the Win32::IPC class. This is meant to\n serve as an abstract base class for other IPC related libraries for MS\n Windows, such as win32-semaphore, win32-event, and so on.\n"
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ none: false
27
+ requirements:
28
+ - - ! '>='
29
+ - !ruby/object:Gem::Version
30
+ version: '0'
31
+ - !ruby/object:Gem::Dependency
32
+ name: test-unit
33
+ requirement: !ruby/object:Gem::Requirement
34
+ none: false
35
+ requirements:
36
+ - - ! '>='
37
+ - !ruby/object:Gem::Version
38
+ version: '0'
39
+ type: :development
40
+ prerelease: false
41
+ version_requirements: !ruby/object:Gem::Requirement
42
+ none: false
43
+ requirements:
44
+ - - ! '>='
45
+ - !ruby/object:Gem::Version
46
+ version: '0'
47
+ description: ! " The win32-ipc library provides the Win32::IPC class. This is meant
48
+ to\n serve as an abstract base class for other IPC related libraries for MS\n
49
+ \ Windows, such as win32-semaphore, win32-event, and so on.\n"
27
50
  email: djberg96@gmail.com
28
51
  executables: []
29
-
30
52
  extensions: []
31
-
32
- extra_rdoc_files:
53
+ extra_rdoc_files:
33
54
  - README
34
55
  - CHANGES
35
56
  - MANIFEST
36
- files:
57
+ files:
37
58
  - CHANGES
38
59
  - lib/win32/ipc.rb
39
60
  - MANIFEST
@@ -41,33 +62,30 @@ files:
41
62
  - README
42
63
  - test/test_win32_ipc.rb
43
64
  - win32-ipc.gemspec
44
- has_rdoc: true
45
65
  homepage: http://www.rubyforge.org/projects/win32utils
46
- licenses:
66
+ licenses:
47
67
  - Artistic 2.0
48
68
  post_install_message:
49
69
  rdoc_options: []
50
-
51
- require_paths:
70
+ require_paths:
52
71
  - lib
53
- required_ruby_version: !ruby/object:Gem::Requirement
54
- requirements:
55
- - - ">="
56
- - !ruby/object:Gem::Version
57
- version: "0"
58
- version:
59
- required_rubygems_version: !ruby/object:Gem::Requirement
60
- requirements:
61
- - - ">="
62
- - !ruby/object:Gem::Version
63
- version: "0"
64
- version:
72
+ required_ruby_version: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ required_rubygems_version: !ruby/object:Gem::Requirement
79
+ none: false
80
+ requirements:
81
+ - - ! '>='
82
+ - !ruby/object:Gem::Version
83
+ version: '0'
65
84
  requirements: []
66
-
67
85
  rubyforge_project: win32utils
68
- rubygems_version: 1.3.5
86
+ rubygems_version: 1.8.23
69
87
  signing_key:
70
88
  specification_version: 3
71
89
  summary: An abstract base class for Windows synchronization objects.
72
- test_files:
90
+ test_files:
73
91
  - test/test_win32_ipc.rb