winipc 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
  SHA256:
3
- metadata.gz: 628224ea1bed3bec0541f134c74f786132ffc060750d07af59e1f97f0273d484
4
- data.tar.gz: af11bf3b0527555f5cc1df9542dee9b8e3c8249d52fbf3999e1b461028d052f5
3
+ metadata.gz: '01972cb47231455db57df188c471e00f7ace66e3721da7ba411125d17acf1760'
4
+ data.tar.gz: 9aa7d8e1e0153cff4167379f023cf8cedea3703228003de14b763889b99f9a61
5
5
  SHA512:
6
- metadata.gz: a8c03faa73dcddedd6727f5784d311c361ebadd4c93efe16ef1519dbae81e6049bbccc5bdf580606338c55dc7d806dbe1ae026146e7a777547f100329b8da497
7
- data.tar.gz: 7ec5eb17dc9d44200e40676b0425865f11a79e5e5faf11d33550e0ca8d2a1d3825bbf40773669640e838c11cda362158193b98c7ba8a684e82f7fa4296b72dea
6
+ metadata.gz: 9a03d3557987c8c38cabb115e711486e4787cfe339d776867f69c3d721b1c418c7d30c6c1fadab525fec493dfceed5b1ffcd4744f7705cb88608a76dd94a5663
7
+ data.tar.gz: 7bcd9c1c5db77486b3a9c9b031ada85f96f0998e2c5a201d7f948fd4124b45f8ba62f9af5a6742e0e87ffaa0190f8ed9efa912c7aeb6eb869761f952b6fa920d
data/README.md CHANGED
@@ -1,154 +1,11 @@
1
- # winipc
2
-
3
- **Windows local IPC for Ruby: named pipes, shared memory, and named synchronization — safe by default, and cooperative under a fiber scheduler.**
4
-
5
- `winipc` is a native extension that exposes the three pillars of Windows
6
- inter-process communication through an ergonomic, hard-to-misuse Ruby API:
7
-
8
- - **Named pipes** — a duplex server and a connect-with-retry client, byte or
9
- message framed. Opened for overlapped I/O, so blocking calls release the GVL
10
- and (under a fiber scheduler such as [winloop](https://rubygems.org/gems/winloop))
11
- run **cooperatively** instead of stalling the loop.
12
- - **Shared memory** — pagefile-backed named regions (`CreateFileMapping`), with
13
- bounds-checked reads and writes.
14
- - **Named synchronization** — cross-process **Mutex**, **Event**, and
15
- **Semaphore**, with auto-releasing block forms.
16
-
17
- It is **secure by default**: pipe servers reject remote clients and restrict the
18
- pipe to the current user (closing the documented default ACL that grants
19
- `Everyone` read), the first instance is created with
20
- `FILE_FLAG_FIRST_PIPE_INSTANCE` so a squatter can't pre-own the name, and no
21
- handle is inheritable.
22
-
23
- ## Requirements
24
-
25
- - **Windows** with a native **MSVC (mswin)** Ruby. Not supported on MinGW/UCRT.
26
- - Visual Studio 2017+ / Build Tools with the **Desktop development with C++** workload.
27
-
28
- ## Install
29
-
30
- ```sh
31
- gem install winipc
32
- ```
33
-
34
- ## Named pipes
35
-
36
- ```ruby
37
- require "winipc"
38
-
39
- # Server (one process)
40
- Winipc::Pipe.listen("myapp/control") do |server|
41
- server.serve do |conn| # accept + yield + close, in a loop
42
- request = conn.read(4096)
43
- conn.write("ack: #{request}")
44
- end
45
- end
46
-
47
- # Client (another process)
48
- Winipc::Pipe.connect("myapp/control", timeout: 5) do |c|
49
- c.write("hello")
50
- puts c.read(4096) # => "ack: hello"
51
- end
52
- ```
53
-
54
- `read` returns `nil` at a clean EOF (peer closed), exactly like `IO#read`; a peer
55
- dying mid-stream raises `Winipc::BrokenPipe`. **Message mode** preserves message
56
- boundaries:
57
-
58
- ```ruby
59
- Winipc::Pipe.listen("svc", mode: :message) { |s| c = s.accept; c.read_message }
60
- Winipc::Pipe.connect("svc", mode: :message) { |c| c.write_message("one whole message") }
61
- ```
62
-
63
- ### Cooperative under winloop
64
-
65
- Because pipe handles are overlapped and the blocking calls release the GVL, a
66
- server and many clients can run as fibers on a single IOCP loop:
67
-
68
- ```ruby
69
- require "winloop"
70
- Winloop.run do
71
- Fiber.schedule { Winipc::Pipe.listen("svc") { |s| s.serve { |c| c.write(c.read(1024)) } } }
72
- Fiber.schedule { Winipc::Pipe.connect("svc") { |c| c.write("hi"); c.read(1024) } }
73
- end
74
- ```
75
-
76
- Under a scheduler, each blocking winipc call is offloaded to a worker thread and
77
- the calling fiber parks, so the loop keeps serving other fibers. With no
78
- scheduler, the same calls just block the calling thread (releasing the GVL).
79
-
80
- ## Shared memory
81
-
82
- ```ruby
83
- # Process A
84
- shm = Winipc::SharedMemory.create("myapp/buffer", 4096)
85
- shm.write(0, "shared payload")
86
-
87
- # Process B
88
- shm = Winipc::SharedMemory.open("myapp/buffer")
89
- shm.read(0, 14) # => "shared payload"
90
- ```
91
-
92
- Reads and writes are bounds-checked (`Winipc::RangeError` outside the region).
93
- Pair shared memory with a named `Event` to hand off deterministically rather
94
- than polling.
95
-
96
- > **Size note:** the creator's `#size` is exactly the requested size; an
97
- > **opener**'s `#size` is the page-rounded mapped region (Windows rounds a
98
- > mapping up to the page boundary and doesn't record the logical size), so an
99
- > opener may read/write within the rounded region. If the exact length matters,
100
- > carry it out-of-band (e.g. store it in the first bytes of the region).
101
-
102
- ## Named synchronization
103
-
104
- ```ruby
105
- # Cross-process mutex
106
- m = Winipc::Mutex.create("myapp/lock")
107
- m.synchronize { critical_section } # always released, even on exception
108
-
109
- # Event (one process waits, another signals)
110
- ev = Winipc::Event.create("myapp/ready", manual_reset: true)
111
- ev.wait(timeout: 10) # => true (signaled) / false (timed out)
112
- ev.signal
113
-
114
- # Counting semaphore
115
- sem = Winipc::Semaphore.create("myapp/slots", initial: 3, maximum: 3)
116
- sem.synchronize { use_a_slot }
117
- ```
118
-
119
- `Mutex` is owned by the acquiring **thread**, so its waits are not offloaded to a
120
- worker (they release the GVL but, under a fiber scheduler, block the loop for the
121
- duration). Use `Event`/`Semaphore` — which any thread may wait on and signal —
122
- for fiber-cooperative coordination. An abandoned mutex (its holder died) is
123
- surfaced as `Winipc::Abandoned` / `#abandoned?` rather than silently ignored.
124
-
125
- ## Errors
126
-
127
- ```
128
- StandardError
129
- └─ Winipc::Error
130
- ├─ Winipc::OSError # a Windows API failed; #code is GetLastError
131
- │ ├─ Winipc::TimeoutError ├─ Winipc::BrokenPipe ├─ Winipc::NotOwner
132
- │ ├─ Winipc::NotFound ├─ Winipc::PipeBusy └─ Winipc::WouldExceedMax
133
- │ ├─ Winipc::Exists ├─ Winipc::Canceled
134
- │ └─ Winipc::AccessDenied
135
- ├─ Winipc::ModeError # byte/message-mode misuse
136
- ├─ Winipc::RangeError # shared-memory access out of bounds
137
- ├─ Winipc::Closed # operating on a closed object
138
- └─ Winipc::Abandoned # a mutex's previous owner died holding it
139
- ```
140
-
141
- ## Notes
142
-
143
- - **Names** are bare (`"myapp/control"`); winipc maps them to `\\.\pipe\…` for
144
- pipes and the `Local\` kernel namespace for shared memory / sync objects (pass
145
- `scope: :global` for the `Global\` namespace, which may need privileges).
146
- - Every kernel handle lives in a wrapper whose finalizer closes it, so a
147
- forgotten `#close` never leaks — but closing explicitly is good manners.
148
- - Blocking calls (`accept`, `read`, `write`, `wait`/`lock`/`acquire`) release the
149
- GVL and are interruptible (`Thread#kill`, `Ctrl-C`, `Timeout`).
150
- - **Windows/MSVC only.** Links `kernel32` + `advapi32`, built with `cl.exe`.
151
-
152
- ## License
153
-
154
- [MIT](LICENSE.txt).
1
+ # winipc (discontinued)
2
+
3
+ **winipc is no longer maintained.** This final release (0.1.1) contains no
4
+ code: installing it compiles nothing, and `require "winipc"` raises a
5
+ `LoadError` explaining the discontinuation.
6
+
7
+ Use UNIXSocket (supported on Windows 10+), or win32-mmap / win32-event / win32-mutex instead.
8
+
9
+ Earlier releases remain on RubyGems only because RubyGems does not allow
10
+ versions older than 30 days to be yanked. They are unmaintained and not
11
+ recommended.
data/lib/winipc.rb CHANGED
@@ -1,338 +1,4 @@
1
- # frozen_string_literal: true
2
-
3
- require "winipc/version"
4
- require "winipc/winipc" # native extension: classes + errors + K_* constants
5
-
6
- # winipc — Windows local IPC for Ruby: named pipes, shared memory, and named
7
- # synchronization objects, with a safe-by-default API.
8
- #
9
- # # named pipe, server side
10
- # Winipc::Pipe.listen("myapp/control") do |server|
11
- # conn = server.accept
12
- # puts conn.read(1024)
13
- # conn.write("ack")
14
- # conn.close
15
- # end
16
- #
17
- # # client side (another process)
18
- # Winipc::Pipe.connect("myapp/control") do |c|
19
- # c.write("hello")
20
- # c.read(1024)
21
- # end
22
- module Winipc
23
- # ---- Win32 flag values (verified) --------------------------------------
24
- PIPE_ACCESS_INBOUND = 0x00000001
25
- PIPE_ACCESS_OUTBOUND = 0x00000002
26
- PIPE_ACCESS_DUPLEX = 0x00000003
27
- FILE_FLAG_OVERLAPPED = 0x40000000
28
- PIPE_TYPE_MESSAGE = 0x00000004
29
- PIPE_READMODE_MESSAGE = 0x00000002
30
- PIPE_WAIT = 0x00000000
31
- PIPE_REJECT_REMOTE_CLIENTS = 0x00000008
32
- PIPE_UNLIMITED_INSTANCES = 255
33
- GENERIC_READ = 0x80000000
34
- GENERIC_WRITE = 0x40000000
35
-
36
- # A Windows API failure carries the originating error code (GetLastError /
37
- # the failing return), set on the exception in C.
38
- class OSError
39
- def code
40
- @code
41
- end
42
- end
43
-
44
- module_function
45
-
46
- # Run a blocking native call cooperatively. Under a Fiber scheduler (e.g.
47
- # winloop) the call is offloaded to a worker Thread so the calling fiber
48
- # parks (Thread#value routes through the scheduler) and the event loop keeps
49
- # serving other fibers; with no scheduler it runs inline (the C call already
50
- # releases the GVL). On fiber unwind the worker is killed+joined so it can't
51
- # leak or consume data destined for a later op.
52
- #
53
- # Caveat: if the fiber is unwound (e.g. Timeout) in the instant after the
54
- # worker acquired a resource but before the value was delivered, that
55
- # acquisition is lost — there is no generic way to hand it back (the same
56
- # inherent limitation as a cancelled read that already pulled bytes).
57
- def run_blocking
58
- sched = Fiber.scheduler
59
- return yield unless sched
60
-
61
- worker = Thread.new do
62
- Thread.current.report_on_exception = false
63
- yield
64
- end
65
- begin
66
- worker.value
67
- ensure
68
- if worker.alive?
69
- worker.kill
70
- worker.join
71
- end
72
- end
73
- end
74
-
75
- # Normalize a bare pipe name to the \\.\pipe\ namespace (pass a full
76
- # \\server\pipe\name through unchanged).
77
- def pipe_path(name)
78
- s = name.to_s
79
- s.start_with?("\\\\") ? s : "\\\\.\\pipe\\#{s}"
80
- end
81
-
82
- # Map a bare object name into the Local\ (default) or Global\ namespace.
83
- def obj_path(name, scope)
84
- prefix = case scope
85
- when :local then "Local\\"
86
- when :global then "Global\\"
87
- else raise ArgumentError, "scope must be :local or :global, got #{scope.inspect}"
88
- end
89
- "#{prefix}#{name}"
90
- end
91
-
92
- def server_access(direction)
93
- case direction
94
- when :duplex then PIPE_ACCESS_DUPLEX
95
- when :inbound then PIPE_ACCESS_INBOUND
96
- when :outbound then PIPE_ACCESS_OUTBOUND
97
- else raise ArgumentError, "direction must be :duplex/:inbound/:outbound"
98
- end
99
- end
100
- private_class_method :server_access
101
-
102
- def client_access(direction)
103
- case direction
104
- when :duplex then GENERIC_READ | GENERIC_WRITE
105
- when :read then GENERIC_READ
106
- when :write then GENERIC_WRITE
107
- else raise ArgumentError, "direction must be :duplex/:read/:write"
108
- end
109
- end
110
- private_class_method :client_access
111
-
112
- def ms_for(timeout)
113
- return -1 if timeout.nil? # INFINITE (block until signaled / connected)
114
-
115
- t = Float(timeout)
116
- raise ArgumentError, "timeout must be non-negative, got #{timeout.inspect}" if t.negative?
117
-
118
- ms = (t * 1000).round
119
- # Never collapse a tiny-but-positive wait into a non-blocking poll.
120
- ms.zero? && t.positive? ? 1 : ms
121
- end
122
-
123
- class Pipe
124
- # Create a named-pipe server. Yields a Listener (auto-closed) if a block is
125
- # given, else returns it.
126
- def self.listen(name, mode: :byte, direction: :duplex, max_instances: :unlimited,
127
- in_buffer: 65_536, out_buffer: 65_536, reject_remote: true, access: :owner)
128
- open_mode = Winipc.send(:server_access, direction) | FILE_FLAG_OVERLAPPED
129
- pipe_mode = PIPE_WAIT
130
- pipe_mode |= PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE if mode == :message
131
- pipe_mode |= PIPE_REJECT_REMOTE_CLIENTS if reject_remote
132
- max = max_instances == :unlimited ? PIPE_UNLIMITED_INSTANCES : Integer(max_instances)
133
- raise ArgumentError, "max_instances must be 1..255" unless (1..255).cover?(max)
134
-
135
- listener = _listen(Winipc.pipe_path(name), open_mode, pipe_mode, max,
136
- Integer(in_buffer), Integer(out_buffer), access)
137
- return listener unless block_given?
138
-
139
- begin
140
- yield listener
141
- ensure
142
- listener.close
143
- end
144
- end
145
-
146
- # Connect to a named-pipe server. Yields a Conn (auto-closed) if a block is
147
- # given, else returns it.
148
- # +timeout+: nil fails fast if the server isn't connectable now; a number of
149
- # seconds retries (server busy or not yet up) until it elapses.
150
- def self.connect(name, mode: :byte, direction: :duplex, timeout: nil)
151
- access = Winipc.send(:client_access, direction)
152
- ms =
153
- if timeout.nil?
154
- 0 # fail fast if the server isn't connectable now
155
- else
156
- t = Float(timeout)
157
- raise ArgumentError, "timeout must be non-negative, got #{timeout.inspect}" if t.negative?
158
-
159
- msr = (t * 1000).round
160
- msr.zero? && t.positive? ? 1 : msr # a tiny positive timeout still waits, not fail-fast
161
- end
162
- conn = Winipc.run_blocking do
163
- _connect(Winipc.pipe_path(name), access, mode == :message, ms)
164
- end
165
- return conn unless block_given?
166
-
167
- begin
168
- yield conn
169
- ensure
170
- conn.close
171
- end
172
- end
173
-
174
- class Listener
175
- # Accept the next client. Blocks (cooperatively under a scheduler). With a
176
- # timeout, returns nil if none connects in time.
177
- def accept(timeout: nil)
178
- Winipc.run_blocking { _accept(Winipc.ms_for(timeout)) }
179
- end
180
-
181
- # Accept clients in a loop, yielding (and closing) each connection.
182
- def serve
183
- loop do
184
- conn = accept
185
- break if conn.nil?
186
-
187
- begin
188
- yield conn
189
- ensure
190
- conn.close
191
- end
192
- end
193
- end
194
- end
195
-
196
- class Conn
197
- # Read up to +maxlen+ bytes (byte mode). Returns a binary String, or nil
198
- # at clean EOF (peer closed).
199
- def read(maxlen)
200
- Winipc.run_blocking { _read(maxlen) }
201
- end
202
-
203
- # Read one whole message (message mode), reassembling across the buffer.
204
- # Returns a binary String or nil at EOF.
205
- def read_message(maxlen = 65_536)
206
- raise ModeError, "winipc: pipe is not in message mode" unless message?
207
-
208
- Winipc.run_blocking { _read_message(maxlen) }
209
- end
210
-
211
- # Write +bytes+; returns the number of bytes written.
212
- def write(bytes)
213
- Winipc.run_blocking { _write(bytes) }
214
- end
215
- alias write_message write # in message mode each write is one message
216
-
217
- def <<(bytes)
218
- write(bytes)
219
- self
220
- end
221
- end
222
- end
223
-
224
- class SharedMemory
225
- # Create a named, pagefile-backed shared-memory region of +size+ bytes.
226
- def self.create(name, size, access: :readwrite, scope: :local, security: :owner)
227
- _create(Winipc.obj_path(name, scope), Integer(size), access != :readonly, security)
228
- end
229
-
230
- # Open an existing named region.
231
- def self.open(name, access: :readwrite, scope: :local)
232
- _open(Winipc.obj_path(name, scope), access != :readonly)
233
- end
234
-
235
- # Flush a range (default: whole region) to backing storage.
236
- def flush(offset = 0, len = nil)
237
- _flush(offset, len)
238
- end
239
- end
240
-
241
- class Mutex
242
- def self.create(name, scope: :local, security: :owner)
243
- _create(K_MUTEX, Winipc.obj_path(name, scope), nil, nil, security)
244
- end
245
-
246
- def self.open(name, scope: :local)
247
- _open(K_MUTEX, Winipc.obj_path(name, scope))
248
- end
249
-
250
- # NOTE: a Windows mutex is owned by the acquiring THREAD, so Mutex waits are
251
- # NOT offloaded to a worker (that would acquire on the wrong thread). They
252
- # release the GVL but, under a fiber scheduler, block the loop for the wait.
253
- # Use Event/Semaphore for fiber-cooperative coordination.
254
- def lock(timeout: nil)
255
- _wait(Winipc.ms_for(timeout)) != :timeout # :ok or :abandoned both acquire
256
- end
257
-
258
- def try_lock
259
- lock(timeout: 0)
260
- end
261
-
262
- def unlock
263
- _unlock
264
- self
265
- end
266
-
267
- def synchronize(timeout: nil)
268
- result = _wait(Winipc.ms_for(timeout))
269
- raise TimeoutError, "winipc: mutex not acquired within timeout" if result == :timeout
270
-
271
- begin
272
- if result == :abandoned
273
- raise Abandoned, "winipc: mutex was abandoned by a dead owner; shared state may be inconsistent"
274
- end
275
-
276
- yield
277
- ensure
278
- _unlock
279
- end
280
- end
281
- end
282
-
283
- class Event
284
- def self.create(name, manual_reset: false, initial: false, scope: :local, security: :owner)
285
- _create(K_EVENT, Winipc.obj_path(name, scope), manual_reset, initial, security)
286
- end
287
-
288
- def self.open(name, scope: :local)
289
- _open(K_EVENT, Winipc.obj_path(name, scope))
290
- end
291
-
292
- # Wait for the event to be signaled. Returns true, or false on timeout.
293
- def wait(timeout: nil)
294
- Winipc.run_blocking { _wait(Winipc.ms_for(timeout)) } == :ok
295
- end
296
- # #signal and #reset are defined in C.
297
- end
298
-
299
- class Semaphore
300
- def self.create(name, initial:, maximum:, scope: :local, security: :owner)
301
- unless maximum.is_a?(Integer) && maximum > 0 && initial.is_a?(Integer) && initial >= 0 && initial <= maximum
302
- raise ArgumentError, "require 0 <= initial <= maximum and maximum > 0"
303
- end
304
-
305
- _create(K_SEM, Winipc.obj_path(name, scope), initial, maximum, security)
306
- end
307
-
308
- def self.open(name, scope: :local)
309
- _open(K_SEM, Winipc.obj_path(name, scope))
310
- end
311
-
312
- # Acquire one unit. Returns true, or false on timeout.
313
- def acquire(timeout: nil)
314
- Winipc.run_blocking { _wait(Winipc.ms_for(timeout)) } == :ok
315
- end
316
-
317
- def try_acquire
318
- acquire(timeout: 0)
319
- end
320
-
321
- # Release +count+ units; returns the previous count.
322
- def release(count = 1)
323
- _release(count)
324
- end
325
-
326
- # Acquire one unit for the duration of the block, then release it. (Use
327
- # #acquire / #release directly for multi-unit patterns.)
328
- def synchronize(timeout: nil)
329
- raise TimeoutError, "winipc: semaphore not acquired within timeout" unless acquire(timeout: timeout)
330
-
331
- begin
332
- yield
333
- ensure
334
- _release(1)
335
- end
336
- end
337
- end
338
- end
1
+ # frozen_string_literal: true
2
+
3
+ raise LoadError, "winipc is discontinued and this release contains no code. Use UNIXSocket (supported on Windows 10+), or win32-mmap / win32-event / win32-mutex instead." \
4
+ " Earlier winipc releases are unmaintained and not recommended."
metadata CHANGED
@@ -1,105 +1,31 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: winipc
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
  - ned
8
8
  bindir: bin
9
9
  cert_chain: []
10
10
  date: 1980-01-02 00:00:00.000000000 Z
11
- dependencies:
12
- - !ruby/object:Gem::Dependency
13
- name: rake
14
- requirement: !ruby/object:Gem::Requirement
15
- requirements:
16
- - - "~>"
17
- - !ruby/object:Gem::Version
18
- version: '13.0'
19
- type: :development
20
- prerelease: false
21
- version_requirements: !ruby/object:Gem::Requirement
22
- requirements:
23
- - - "~>"
24
- - !ruby/object:Gem::Version
25
- version: '13.0'
26
- - !ruby/object:Gem::Dependency
27
- name: rake-compiler
28
- requirement: !ruby/object:Gem::Requirement
29
- requirements:
30
- - - "~>"
31
- - !ruby/object:Gem::Version
32
- version: '1.2'
33
- type: :development
34
- prerelease: false
35
- version_requirements: !ruby/object:Gem::Requirement
36
- requirements:
37
- - - "~>"
38
- - !ruby/object:Gem::Version
39
- version: '1.2'
40
- - !ruby/object:Gem::Dependency
41
- name: minitest
42
- requirement: !ruby/object:Gem::Requirement
43
- requirements:
44
- - - "~>"
45
- - !ruby/object:Gem::Version
46
- version: '5.0'
47
- type: :development
48
- prerelease: false
49
- version_requirements: !ruby/object:Gem::Requirement
50
- requirements:
51
- - - "~>"
52
- - !ruby/object:Gem::Version
53
- version: '5.0'
54
- - !ruby/object:Gem::Dependency
55
- name: vcvars
56
- requirement: !ruby/object:Gem::Requirement
57
- requirements:
58
- - - "~>"
59
- - !ruby/object:Gem::Version
60
- version: '0.1'
61
- - - ">="
62
- - !ruby/object:Gem::Version
63
- version: 0.1.1
64
- type: :development
65
- prerelease: false
66
- version_requirements: !ruby/object:Gem::Requirement
67
- requirements:
68
- - - "~>"
69
- - !ruby/object:Gem::Version
70
- version: '0.1'
71
- - - ">="
72
- - !ruby/object:Gem::Version
73
- version: 0.1.1
74
- description: |
75
- winipc is a native extension that exposes Windows local inter-process
76
- communication through an ergonomic, safe-by-default Ruby API: duplex named
77
- pipes (byte and message mode, with a server and a connect-with-retry client),
78
- pagefile-backed shared memory via named file mappings, and named
79
- synchronization objects (mutex, event, semaphore). Pipe handles are opened
80
- for overlapped I/O so they cooperate with a fiber scheduler, and objects are
81
- created with a restrictive security descriptor by default. Windows MSVC
82
- (mswin) Ruby only.
11
+ dependencies: []
12
+ description: winipc is discontinued. This final release contains no code and exists
13
+ only so that installing winipc explains the discontinuation instead of building
14
+ an unmaintained native extension.
83
15
  executables: []
84
- extensions:
85
- - ext/winipc/extconf.rb
16
+ extensions: []
86
17
  extra_rdoc_files: []
87
18
  files:
88
- - CHANGELOG.md
89
19
  - LICENSE.txt
90
20
  - README.md
91
- - ext/winipc/extconf.rb
92
- - ext/winipc/winipc.c
93
21
  - lib/winipc.rb
94
- - lib/winipc/version.rb
95
- homepage: https://github.com/main-path/winipc
96
22
  licenses:
97
23
  - MIT
98
24
  metadata:
99
- homepage_uri: https://github.com/main-path/winipc
100
- changelog_uri: https://github.com/main-path/winipc/blob/main/CHANGELOG.md
101
- bug_tracker_uri: https://github.com/main-path/winipc/issues
102
25
  rubygems_mfa_required: 'true'
26
+ post_install_message: winipc is discontinued and this release contains no code. Use
27
+ UNIXSocket (supported on Windows 10+), or win32-mmap / win32-event / win32-mutex
28
+ instead.
103
29
  rdoc_options: []
104
30
  require_paths:
105
31
  - lib
@@ -107,7 +33,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
107
33
  requirements:
108
34
  - - ">="
109
35
  - !ruby/object:Gem::Version
110
- version: '3.0'
36
+ version: '2.0'
111
37
  required_rubygems_version: !ruby/object:Gem::Requirement
112
38
  requirements:
113
39
  - - ">="
@@ -116,6 +42,6 @@ required_rubygems_version: !ruby/object:Gem::Requirement
116
42
  requirements: []
117
43
  rubygems_version: 3.6.9
118
44
  specification_version: 4
119
- summary: 'Windows local IPC for Ruby: named pipes, shared memory, and named synchronization
120
- objects.'
45
+ summary: Discontinued. Use UNIXSocket (supported on Windows 10+), or win32-mmap /
46
+ win32-event / win32-mutex instead.
121
47
  test_files: []
data/CHANGELOG.md DELETED
@@ -1,29 +0,0 @@
1
- # Changelog
2
-
3
- ## [0.1.0] - 2026-06-01
4
-
5
- Initial release.
6
-
7
- - **Named pipes** — `Winipc::Pipe.listen` (server with `#accept`/`#serve`) and
8
- `Winipc::Pipe.connect` (connect-with-retry client); a duplex `Conn` with
9
- `#read`/`#write` (byte mode) and `#read_message`/`#write_message` (message
10
- mode). Overlapped I/O throughout: blocking calls release the GVL and are
11
- cancelable, and run cooperatively under a fiber scheduler (winloop) by
12
- offloading to a worker thread. The listener always keeps a pending instance so
13
- clients never see a between-accepts gap.
14
- - **Shared memory** — `Winipc::SharedMemory.create`/`.open` over pagefile-backed
15
- named file mappings, with bounds-checked `#read`/`#write`/`#flush`.
16
- - **Named synchronization** — `Winipc::Mutex`, `Winipc::Event`, and
17
- `Winipc::Semaphore` with `create`/`open`, GVL-releasing interruptible waits,
18
- and auto-releasing `#synchronize` block forms. Abandoned mutexes are surfaced.
19
- - **Secure by default** — pipe servers restrict access to the current user,
20
- reject remote clients, and use `FILE_FLAG_FIRST_PIPE_INSTANCE`; handles are
21
- non-inheritable; objects default to the per-session `Local\` namespace. An
22
- explicit SDDL string or `:everyone` is opt-in.
23
- - Error taxonomy under `Winipc::Error` (`OSError` with `#code`, plus
24
- `TimeoutError`, `NotFound`, `Exists`, `AccessDenied`, `BrokenPipe`, `PipeBusy`,
25
- `Canceled`, `NotOwner`, `WouldExceedMax`, `ModeError`, `RangeError`, `Closed`,
26
- `Abandoned`).
27
-
28
- Pure C extension over kernel32 + advapi32 (`rb_raise`/longjmp-safe; every handle
29
- freed by a TypedData finalizer). Windows MSVC (mswin) Ruby only.