- First Steps with AnyEvent
- Emulating POSIX Signals
- List Processes AKA ps
- Bad AnyEvent Install
When I tried to move my AnyEvent-based code over to Windows today, I got a slightly unpleasant surprise.
$ perl kernel.pl fcntl is not implemented at c:/strawberry/perl/site/lib/AnyEvent/Util.pm line 362.
(Actually, that path is not accurate – it is from simulating the problem on a different computer. The actual path is a network share.)
Drat I thought, maybe AnyEvent isn’t as portable as I thought. But hang on a minute. When doubting a widely used library ahead of your own code, it is usually your code that is at fault. In my experience anyhow. And besides, Twiggy runs on Windows. What have I done wrong?
So I had a look at the offending line of code.
# Sets the blocking state of the given filehandle (true == nonblocking, # false == blocking). Uses fcntl on anything sensible and ioctl FIONBIO on # broken (i.e. windows) platforms. BEGIN { *fh_nonblocking = AnyEvent::WIN32 ? sub($$) { ioctl $_[0], 0x8004667e, pack "L", $_[1]; # FIONBIO } : sub($$) { fcntl $_[0], AnyEvent::F_SETFL, $_[1] ? AnyEvent::O_NONBLOCK : 0; } ; }
While I was there, a quick glance down the comments gave me a picture of someone who enjoys working on Windows as much as I do
# perl's socketpair emulation fails on many vista machines, because # vista returns fantasy port numbers. ... # vista has completely broken peername/sockname that return # fantasy ports. this combo seems to work, though. ... # vista example (you can't make this shit up...):
I quickly tracked the root of the problem down to a file called constants.pl. Uh oh, take a look at the WIN32 subroutine.
package AnyEvent; sub CYGWIN () { 0 } sub WIN32 () { 0 } ...
That is actually a file generated from a Unix install of AnyEvent. constants.pl is generated at make time from a file called constants.pl.PL that looks like this.
sub i($$) { print "sub $_[0] () { ", $_[1]*1, " }\n"; } print "package AnyEvent;\n"; our $WIN32 = $^O =~ /mswin32/i; i CYGWIN => $^O =~ /cygwin/i; i WIN32 => $WIN32; ...
And this is down to my company’s perl policy. A number of pure perl modules are installed on a windows network share which is samba mounted onto the unix servers. We’re primarily a C++/Java shop and Perl is something of a second-class citizen but there are a good number of modules available (including Moose, AnyEvent, POE, etc.)
The downside is that it is hard to get modules installed that aren’t there already, and we are not permitted to download them ourselves. Most of the time, this isn’t a problem.
Anyway, I tried to fix this in my own script using variants on:
package AnyEvent; use constant WIN32 => 1; package Kernel;
and
sub AnyEvent::WIN32 () { 1 }
both before and after the use AnyEvent lines. I couldn’t get round it though. This happened when the definitions were after the use lines.
$ perl kernel.pl Constant subroutine AnyEvent::WIN32 redefined at c:/strawberry/perl/lib/constant.pm line 131. Constant subroutine AnyEvent::WIN32 redefined at kernel.pl line 23. fcntl is not implemented at c:/strawberry/perl/site/lib/AnyEvent/Util.pm line 362.
And this is when it was placed before.
$ perl kernel.pl Constant subroutine WIN32 redefined at c:/strawberry/perl/site/lib/AnyEvent/constants.pl line 3. fcntl is not implemented at c:/strawberry/perl/site/lib/AnyEvent/Util.pm line 362.
How frustrating!
1. send bug report to developer
2. patch constants.pl
This problem intrigued me. I always found it magical how you can override another module’s symbol table entry, but the only module it will affect is your own! I mean you only notice a difference if you call that changed symbol table entry (subroutine) from your module.
Anyways, the only way I could think of to bypass constants was for you to make a YOUR OWN constant.pm file and have it act as a proxy between the real constant.pm and everyone else.
Check out my gist: http://gist.github.com/381788
I finally got to use @INC hooks… the horror! It’s really funky but it works!
Hi zloyrusskiy,
I don’t think this is a bug, at least not in AnyEvent. It is an artefact that results from the slightly whacky way my firm installs perl modules.
And I don’t have write access to the constants.pl file unfortunately.
Hi juster,
That is a cool solution. My thought was the slightly more boring generate my own constants.pl file and make sure it was ahead of the real constant.pl in @INC (although I haven’t tried that yet).
I think constant.pl must define WIN32 sub like:
eval sprintf ‘sub WIN32 { %s }’,$^O eq ‘MSWin32′;
sub WIN32;
and there is no error in your case and no performance penalty to AnyEvent
Oh whoops I see now that AnyEvent isn’t using constant.pm so my code isn’t very helpful!
I find require hooks fascinating. I have updated my gist so it would actually help you… But copy/paste is more straightforward and it’s good to keep things simple!
re: zloyrusskiy
You forgot the constant prototype ie: sub WIN32 () { … }
I’m guessing this is why the constant.pl is generated in the first place. If there is an if statement inside sub WIN32 () {} it will not be inlined. Hence the if statement is converted to its result when creating the constant function.
I checked my example, it works.
Your example is good, but hard.
use lib qw(author_path_to_AnyEvent\constants.pl);
use AnyEvent;
no lib qw(author_path_to_AnyEvent\constants.pl);
I was checked this it works.
@zloyrusskiy – thanks for the solution it looks good. I still don’t think I would say there is a bug in AnyEvent despite the fact it is solvable. Installing on one Perl on Unix and expecting it to work on a Windows Perl is a little unreasonable. What do you think?
Anyway, I’ll get your solution off to Marc and see if he wants to incorporate it into a later version.
@juster – gosh, that solution looks complicated. First good chance to use require hooks eh?
I’ll have to look at it in detail to understand it.
Indeed, this is not a bug in AnyEvent – compiling a module on a different operating only works by pure chance. A module doesn’t need to contain any C code to be architecture-dependent.
While there are ways to redefine WIN32 so that AnyEvent sees it, this does nothing to the other constants, which are likely to be very different.
The only correct method is to not share arch-dependent modules between operating systems (you wouldn’t expect the perl binary from unix to run under windows, either…)