win32-ipc 0.5.3 → 0.6.0

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.
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