Repository navigation
Windows and CLR interop fixes: boxed struct fields, zlib, File::NULL, Process.kill, JSON control characters - #25
Merged
Conversation
RubyFieldInfo built a field store as Assign(Field(Convert(target, S), f), v).
Convert unboxes into a temporary, so once the rule is compiled the store
changes that copy and the box Ruby holds keeps its old fields - silently.
OpenSSL::PKey fills RSAParameters / DSAParameters / ECParameters this way, so
a PEM loaded about 35 times and then failed for the rest of the process
("The specified RSA parameters are not valid"): the interpreter updated the
box, compiled code did not. With today's upstream DLR the interpreter copies
too (IronLanguages/dlr#368), and it fails on the first load.
The store now goes through Unbox, which addresses the value inside the box,
in compiled and interpreted code alike. Calls to mutating methods on a boxed
struct already reached the box.
ClrValueTypes1, commented out since 2011 as "fails with /noadaptive", is the
same bug and runs again; ClrValueTypes3 fills S2 and RSAParameters in a method
called 100 times and checks every result.
Time_MachineZone1 compares Time.at, Time.local and Time.now with what TimeZoneInfo.Local says for a winter and a summer instant, so it holds in any machine zone, and checks that a local time's zone survives Marshal. Time_OlsonZone1 sets TZ=Europe/Zurich, which Windows resolves through TimeZoneInfo since it has no zoneinfo files. Both fail on 4.0.1 on Windows, where the zone was TimeZoneInfo.Local's base offset alone.
require "zlib" was a LoadError on Windows: there is no libz there, and the binding's z_stream is laid out for LP64. Every .NET runtime carries zlib in System.IO.Compression.Native, though (zlib-ng since .NET 9, Intel's zlib in .NET 8), behind CompressionNative_* exports over its own PAL_ZStream. ClrZlib runs LibZ's entry points on those exports, so ZStream, Inflate, Deflate and the gzip classes in zlib.rb work unchanged: the z_stream record is only managed code's on Windows; next_in/avail_in/next_out/avail_out go to the PAL stream and back, and total_in, total_out and adler (the running adler32/crc32, or the dictionary id on Z_NEED_DICT) are kept as libz keeps them. adler32, crc32, their _combine and crc_table are computed as zlib does. A reset begins the stream again with its parameters (.NET 8 exports no reset). What the exports leave out raises NotImplementedError: set_dictionary, Deflate#params once data has gone in, Inflate#sync_point?. Linux and macOS keep calling libz exactly as before. ruby/spec library/zlib on Windows: 148 examples, 2 failures (they compare classic zlib's exact compressed bytes), 3 errors (the NotImplementedErrors); before, all 42 files were the LoadError. Zlib_Streams1 inflates input CRuby compressed and checks round trips, totals, adler and the checksums, so it holds on libz and on .NET's zlib alike.
Process.spawn(cmd, out: File::NULL) failed on Windows with ENOENT, and Process.kill raised System::DllNotFoundException. IO::NULL (and File::NULL) came from the prelude as "/dev/null" everywhere; Windows has no such file, so the redirect could not be opened. It is "NUL" there, as in CRuby. The error itself named the program, not the file: WindowsSpawn handed back only -errno and the prelude filled in the command. A redirection that cannot be opened now raises with its own path - ENOENT, EACCES, or EISDIR for a directory - as CRuby does, which opens it in the parent. Process.kill P/Invoked libc's kill(), which Windows does not have. WindowsSpawn.Kill does what CRuby's win32 kill() does: KILL is TerminateProcess (exit code 0, as there), 0 reports whether the process still runs (ESRCH once it has exited), INT is a console Ctrl+C - Ctrl+Break aimed at a process group - and every other signal is EINVAL. ruby/spec on Windows: core/process/kill 2 errors -> 0; core/process/spawn, core/kernel/spawn and core/kernel/system unchanged otherwise. Process_SpawnKill1 spawns a child that blocks on a pipe, signals 0 and KILL, waits, and checks a missing redirection is named.
RFC 8259 has U+0000-U+001F escaped inside a string, and the json gem raises "invalid ASCII control character in string" for an unescaped one. IronRuby's parser took them as they were, so a document with a literal line break in a value parsed here and failed on CRuby - code that falls back on a ParserError for exactly that case never ran its fallback. JSON.parse(source, allow_control_characters: true), the gem's option since 2.18.0 (the API version this JSON reports), lets them through. DEL and escaped control characters stay fine.
net/http is said to be undependable on Windows because IO.select on a socket is poll(2). Measured: plain sockets are fine (Socket.Select on Windows, and read_timeout fires); over TLS nothing times out - SSLSocket's read_nonblock, write_nonblock and connect_nonblock block in SslStream and never return :wait_readable, which is the only path net/protocol applies read_timeout on - and ca_file/cert_store/verify_callback are not consulted. Docs/net-http-over-tls.md has the diagnosis and a two-stage proposal: timeouts on the blocking SslStream calls plus the context's CAs first (small), real non-blocking reads through IoReadiness later. Util/net-http-tls-server.rb (CRuby) and Util/net-http-tls-check.rb reproduce both findings.
|
Important
This repository does not receive automatic reviews because it has fewer than 10 stars.
|
…-windows # Conflicts: # Src/IronRuby.Tests/RubyTests.cs
Raw inflate on Windows / .NET 8 stopped after the first 16 KB: the run loop ends an inflate run once avail_in is 0, which with libz means it has nothing more to give. .NET 8's zlib is Intel's fork, which reads all of the input ahead into its bit buffer, so with the output full and no input left it can still have output to hand out. A zlib or gzip stream hid this, because its trailer was still unread. On .NET's zlib the loop asks again; when inflate really is done it leaves the fresh buffer untouched and the avail_out check ends the run. libz keeps the old condition. Zlib_Streams1 caught it on CI (windows-latest net8.0); with this its output matches CRuby's on .NET 8, and ruby/spec library/zlib there has 0 failures and the 3 NotImplementedErrors.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Five fixes for problems that show up when IronRuby code runs as a long-lived plugin inside a Windows .NET service, plus a diagnosis of
net/httpover TLS. Each fix has a test in IronRuby.Tests that fails without it.Fixes
RubyFieldInfo).s = SomeStruct.new; s.Field = xwas built asAssign(Field(Convert(target))), andConvertunboxes into a temporary. Once a call site was compiled, the store was lost silently.OpenSSL::PKeyfillsRSAParametersthis way, so loading the same PEM failed after about 35 loads ("The specified RSA parameters are not valid"); with the current upstream DLR it fails on the first. Stores now go throughUnbox.ClrValueTypes1, commented out since 2011 as "fails with /noadaptive", runs again;ClrValueTypes3fills a struct andRSAParametersin a method called 100 times.require "zlib"was aLoadError, because there is no libz.ClrZlibruns the sameLibZentry points on the zlib that .NET itself ships (System.IO.Compression.Native), soZStream,Inflate,Deflateand the gzip classes work unchanged. Linux and macOS keep calling libz exactly as before. What .NET does not export raisesNotImplementedError:set_dictionary,Deflate#paramsonce data has gone in, andInflate#sync_point?. The README says so.IO::NULLis"NUL"on Windows, as in CRuby; it was"/dev/null", sospawn(..., out: File::NULL)failed. A redirection that cannot be opened now names the file, not the program (ENOENT, EACCES or EISDIR, using CRuby'smap_errnochoices).Process.killon Windows P/Invoked libc'skill()and raisedDllNotFoundException. It now follows CRuby's win32kill():KILLisTerminateProcess(exit code 0, as in CRuby), and0asks whether the process is still running.INTis a console Ctrl+C, or Ctrl+Break for a process group.EINVAL.JSON.parserejects raw control characters (U+0000–U+001F) inside strings, as the json gem does, and supports the gem'sallow_control_characters: true(2.18.0).Tests only
Time_MachineZone1andTime_OlsonZone1cover the Windows zone fix from Time: follow daylight saving time on Windows #19. They compare withTimeZoneInfoitself, so they hold in any machine zone.Diagnosis only
Docs/net-http-over-tls.md: on Windows,net/httpover plain sockets is fine (IO.selecton a socket isSocket.Select, notpoll(2)). Over TLS, read, write and open timeouts never fire, andca_file/cert_storeare ignored. The doc proposes a two-stage fix;Util/net-http-tls-server.rb(CRuby) andUtil/net-http-tls-check.rbreproduce the problem. The fix itself is a separate branch stacked on this one.Testing
Run on Windows 11, .NET 10, Debug only; Linux, macOS, net8.0 and Release are left to CI.
core/time,core/file,core/process,core/kernel,core/marshal,core/signal,library/zlib,library/opensslandlibrary/stringio:library/zlibnow loads: 148 examples, 2 failures and 3 errors. The failures compare classic zlib's exact output bytes; the errors are the threeNotImplementedErrors.core/process/kill: 2 errors → 0.Notes
Src/IronRuby.Tests/RubyTests.cs.