Skip to content

After FdGetOsFHandle() failure, lastFileError() is misleading #14

@nmisch

Description

@nmisch

The pod documentation mentions lastFileError() reacting to FdGetOsFHandle(),
and File.xs indeed has code to save GetLastError() in that case. I tried
that as a workaround for issue #13. However, _get_osfhandle() does not
document GetLastError() being meaningful. Indeed, my experiments show that a
failing _get_osfhandle() can make GetLastError() return zero, among other
values. Test program:

use strict;
use warnings;
use Test::More;
use Win32API::File();
#use Socket;  # changes the error codes further

my $improbable_error_code = 15614;  # ERROR_PACKAGE_REPOSITORY_CORRUPTED
my($before, $after);

close STDIN;
for my $winsock_active (0, 1) {
  require Socket if $winsock_active;
  # fd 0 is closed but below the highest open fd
  # fd 100000 is closed and above the highest open fd
  # fd 1 is open
  for my $fd (0, 100000, 1) {
    Win32API::File::fileLastError($improbable_error_code);
    $before = 0+Win32API::File::fileLastError();
    Win32API::File::FdGetOsFHandle($fd);
    $after = 0+Win32API::File::fileLastError();
    is($after, $before, "winsock=$winsock_active fd=$fd");
  }
}

Output:

not ok 1 - winsock=0 fd=0
#   Failed test 'winsock=0 fd=0'
#   at //tsclient/remote/demo_closed.pl line 21.
#          got: '6'
#     expected: '15614'
not ok 2 - winsock=0 fd=100000
#   Failed test 'winsock=0 fd=100000'
#   at //tsclient/remote/demo_closed.pl line 21.
#          got: '203'
#     expected: '15614'
ok 3 - winsock=0 fd=1
not ok 4 - winsock=1 fd=0
#   Failed test 'winsock=1 fd=0'
#   at //tsclient/remote/demo_closed.pl line 21.
#          got: '0'
#     expected: '15614'
not ok 5 - winsock=1 fd=100000
#   Failed test 'winsock=1 fd=100000'
#   at //tsclient/remote/demo_closed.pl line 21.
#          got: '203'
#     expected: '15614'
ok 6 - winsock=1 fd=1
# Tests were run but no plan was declared and done_testing() was not seen.

I see the following ways of detecting FdGetOsFHandle() failure today, given
the need to work around issue #13 and $SUBJECT:

  1. Test whether Perl is 32-bit. If 32-bit, compare the return value to
    0xffffffff. Otherwise, compare the return value to 0xffffffffffffffff.
    No known disadvantages.

  2. Use a sequence like:

    fileLastError($improbable_error_code);
    FdGetOsFHandle(...);
    if ($improbable_error_code == fileLastError()) { # succeeded ...

    In practice, I expect _get_osfhandle() can set just a few of the thousands
    of error codes, making all others viable as $improbable_error_code. Having
    said that, it's infeasible to prove that GetLastError() will never report
    $improbable_error_code after _get_osfhandle().

  3. Test $!==9 (EBADF). _get_osfhandle() documents this, and I have observed
    it to work. The risk would be FdGetOsFHandle() somehow making an
    additional library call that clobbers errno. That risk feels significant.

I will likely use (1) for the moment.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions