in reply to Re^4: Non-blocking Reads from Pipe Filehandle
in thread Non-blocking Reads from Pipe Filehandle

It's definitely a necessity even though though it could be fudged this time.

For the record. This is from API.XS:

case T_POINTER: ## If pointer params[i].t = T_POINTER; origST[i] = ST(i+1); if(has_proto) { pointerCallPack(ST(i+1), i, intypes); params[i].p = (char *) SvPV_nolen(ST(i+1)); } else { ## and no proto if(SvIOK(ST(i+1)) && SvIV(ST(i+1)) == 0) { ## and IV is OK +and zero params[i].p = NULL; ## use NULL } else { params[i].p = (char *) SvPV_nolen(ST(i+1)); ## else use th +e address of the PV } }

So, if you work with the module instead of fighting it, and use 0 (zero) instead of undef to indicate that you want this pointer parameter set to null, that's what it does!

Ie. it's not a "fudge", but how the author designed it!


Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
"Science is about questioning the status quo. Questioning authority".
In the absence of evidence, opinion is indistinguishable from prejudice.
"Too many [] have been sedated by an oppressive environment of political correctness and risk aversion."

Replies are listed 'Best First'.
Re^6: ... (Proof!)
by ikegami (Patriarch) on Oct 01, 2008 at 04:00 UTC

    I used 0. Didn't work. But then again, has_proto is true for the snippet that shows the problem. What's getting you so excited?

    Update: Bug report. Proposed change:

    case T_POINTER: params[i].t = T_POINTER; origST[i] = ST(i+1); if(has_proto) { - pointerCallPack(ST(i+1), i, intypes); - params[i].p = (char *) SvPV_nolen(ST(i+1)); + if(SvOK(ST(i+1)) { + pointerCallPack(ST(i+1), i, intypes); + params[i].p = (char *) SvPV_nolen(ST(i+1)); + } else { + params[i].p = NULL; + } } else { if(SvIOK(ST(i+1)) && SvIV(ST(i+1)) == 0) { params[i].p = NULL; } else { params[i].p = (char *) SvPV_nolen(ST(i+1)); } } ... if(params[i].t == T_POINTER && has_proto) { - pointerCallUnpack(origST[i], i, intypes); + if(SvOK(origST[i]) { + pointerCallUnpack(origST[i], i, intypes); + } }
      I used 0. Didn't work.

      Probably because your protoype is wrong.

      This: my $f = Win32::API->new('kernel32', 'PeekNamedPipe', 'LLLLLL', 'L')

      Should be this: my $f = Win32::API->new('kernel32', 'PeekNamedPipe', 'LPLPPP', 'L')

      Fix that, and your 'fix' above is unnecessary.

      Just another hack at a module that worked fine for the most part before people start trying 'fix it' without enough knowledge to do so.

      I wouldn't call it excitement. More depression when I see people proffering this kind unnecessary complexity:

      sub get_pv { local $^W; unpack 'L!', pack 'P', $_[0] } BEGIN { # BOOL PeekNamedPipe( # HANDLE hNamedPipe, # LPVOID lpBuffer, # DWORD nBufferSize, # LPDWORD lpBytesRead, # LPDWORD lpTotalBytesAvail, # LPDWORD lpBytesLeftThisMessage # ) my $f = Win32::API->new('kernel32', 'PeekNamedPipe', 'LLLLLL', 'L') or die $^E; sub PeekNamedPipe { my $nBytesRead; my $nTotalBytesAvail; my $nBytesLeftThisMessage; $nBytesRead = pack('L!', $_[3]) if defined $_[3]; $nTotalBytesAvail = pack('L!', $_[4]) if defined $_[4]; $nBytesLeftThisMessage = pack('L!', $_[5]) if defined $_[5]; my $rv = $f->Call( $_[0], get_pv($_[1]), $_[2], get_pv($nBytesRead), get_pv($nTotalBytesAvail), get_pv($nBytesLeftThisMessage), ); $_[3] = unpack('L!', $nBytesRead ) if defined $_[3]; $_[4] = unpack('L!', $nTotalBytesAvail ) if defined $_[4]; $_[5] = unpack('L!', $nBytesLeftThisMessage) if defined $_[5]; return $rv; } }

      for the sake of a little reading, and a couple of zeros. The icing on the cake is when they try to claim the technical high ground as justifiction for it.


      Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
      "Science is about questioning the status quo. Questioning authority".
      In the absence of evidence, opinion is indistinguishable from prejudice.

        My original code, the one with the problem, was

        Win32::API->Import( 'kernel32', 'BOOL PeekNamedPipe( HANDLE hNamedPipe, LPVOID lpBuffer, DWORD nBufferSize, LPDWORD lpBytesRead, LPDWORD lpTotalBytesAvail, LPDWORD lpBytesLeftThisMessage )', );

        There's no way to pass NULL when using that syntax. Using that syntax is what sets has_proto. I may not have found the optimal *workaround*, but that doesn't mean the bug isn't there. I didn't use LPLPPP because I assumed it was buggy too.

        More depression when I see people proffering this kind unnecessary complexity:

        If you think LPLPPP is shorter...

        BEGIN { # BOOL WINAPI PeekNamedPipe( # __in HANDLE hNamedPipe, # __out_opt LPVOID lpBuffer, # __in DWORD nBufferSize, # __out_opt LPDWORD lpBytesRead, # __out_opt LPDWORD lpTotalBytesAvail, # __out_opt LPDWORD lpBytesLeftThisMessage # ) my $f = Win32::API->new('kernel32', 'PeekNamedPipe', 'LPLPPP', 'L') or die $^E; sub PeekNamedPipe { my $vBuffer = defined($_[1]) ? $vBuffer : 0; my $nBytesRead = defined($_[3]) ? pack('L!', $_[3]) : 0; my $nTotalBytesAvail = defined($_[4]) ? pack('L!', $_[4]) : 0; my $nBytesLeftThisMsg = defined($_[5]) ? pack('L!', $_[5]) : 0; my $rv = $f->Call( $_[0], $vBuffer, $_[2], $nBytesRead, $nTotalBytesAvail, $nBytesLeftThisMsg, ); $_[1] = $vBuffer if defined $_[1]; $_[3] = unpack('L!', $nBytesRead ) if defined $_[3]; $_[4] = unpack('L!', $nTotalBytesAvail ) if defined $_[4]; $_[5] = unpack('L!', $nBytesLeftThisMsg) if defined $_[5]; return $rv; } }

        That's why I prefer to use the prototype approach. It's much simpler. It handles packing pointers. It just doesn't handle NULL.

        I agree with you. Avoiding this complexity would be much better, but a bug is preventing me.

Re^6: ... (Proof!)
by ikegami (Patriarch) on Oct 01, 2008 at 06:25 UTC

    it's not a "fudge"

    lpTotalBytesAvail and lpBytesLeftThisMessage are both LPDWORD. You pass zero to both. Yet, you want me to believe doing so results in passing a non-NULL pointer for lpTotalBytesAvail and a NULL pointer for lpBytesLeftThisMessage?

      No. Both are passed as NULL-pointers:

      c:\test>buk-pipe.pl (XS)Win32::API::Call: prototype? 0 [n/a] (XS)Win32::API::Call: params[0].t=1, .u=76 (XS)Win32::API::Call: params[1].t=2, .u= (XS)Win32::API::Call: params[2].t=1, .u=1024 (XS)Win32::API::Call: params[3].t=2, .u=(null) (XS)Win32::API::Call: params[4].t=2, .u=(null) (XS)Win32::API::Call: params[5].t=2, .u=(null) (XS)Win32::API::Call: parameter 5 (P) is (null) (XS)Win32::API::Call: parameter 4 (P) is (null) (XS)Win32::API::Call: parameter 3 (P) is (null) (XS)Win32::API::Call: parameter 2 (N) is 1024 (XS)Win32::API::Call: parameter 1 (P) is (XS)Win32::API::Call: parameter 0 (N) is 76 (XS)Win32::API::Call: Calling ApiFunctionInteger() (XS)Win32::API::Call: ApiFunctionInteger returned 1 (XS)Win32::API::Call: freeing memory... (XS)Win32::API::Call: returning to caller. (XS)Win32::API::Call: returning 1. ...

      If you look closely, you'll see that I am not actually using lpTotalBytesAvail / $cAvail. I was in an early cut of the code, but then I realised that I didn't need to. It is easier to extract the information I need directly from the buffer.

      When I was using $cAvail, it was coded like to this:

      sub readlineMaybe { my $fh = shift; my $osfh = GetOsFHandle( $fh ) or die $^E; my( $bufsize, $buffer, $cAvail, $read ) = ( 1024, chr(0)x1024, chr +(0)x4, 0 ); PeekNamedPipe( $osfh, $buffer, $bufsize, $read, $cAvail, 0 ) or $^E == 109 or die $^E; print 'Available: ', unpack 'V', $cAvail; return if $^E == 109; my $eolPos = 1+index $buffer, $/; return '' unless $eolPos; sysread( $fh, $buffer, $eolPos ) or die $!; return $buffer; }

      Which produces this output:

      c:\test>buk-pipe.pl (XS)Win32::API::Call: prototype? 0 [n/a] (XS)Win32::API::Call: params[0].t=1, .u=76 (XS)Win32::API::Call: params[1].t=2, .u= (XS)Win32::API::Call: params[2].t=1, .u=1024 (XS)Win32::API::Call: params[3].t=2, .u=(null) (XS)Win32::API::Call: params[4].t=2, .u= (XS)Win32::API::Call: params[5].t=2, .u=(null) (XS)Win32::API::Call: parameter 5 (P) is (null) (XS)Win32::API::Call: parameter 4 (P) is (XS)Win32::API::Call: parameter 3 (P) is (null) (XS)Win32::API::Call: parameter 2 (N) is 1024 (XS)Win32::API::Call: parameter 1 (P) is (XS)Win32::API::Call: parameter 0 (N) is 76 (XS)Win32::API::Call: Calling ApiFunctionInteger() (XS)Win32::API::Call: ApiFunctionInteger returned 1 (XS)Win32::API::Call: freeing memory... (XS)Win32::API::Call: returning to caller. (XS)Win32::API::Call: returning 1. Available: 0 (XS)Win32::API::Call: prototype? 0 [n/a] (XS)Win32::API::Call: params[0].t=1, .u=76 (XS)Win32::API::Call: params[1].t=2, .u= (XS)Win32::API::Call: params[2].t=1, .u=1024 (XS)Win32::API::Call: params[3].t=2, .u=(null) (XS)Win32::API::Call: params[4].t=2, .u= (XS)Win32::API::Call: params[5].t=2, .u=(null) (XS)Win32::API::Call: parameter 5 (P) is (null) (XS)Win32::API::Call: parameter 4 (P) is (XS)Win32::API::Call: parameter 3 (P) is (null) (XS)Win32::API::Call: parameter 2 (N) is 1024 (XS)Win32::API::Call: parameter 1 (P) is (XS)Win32::API::Call: parameter 0 (N) is 76 (XS)Win32::API::Call: Calling ApiFunctionInteger() (XS)Win32::API::Call: ApiFunctionInteger returned 1 (XS)Win32::API::Call: freeing memory... (XS)Win32::API::Call: returning to caller. (XS)Win32::API::Call: returning 1. Available: 37 Got: 'Wed Oct 1 08:32:59 2008: some text' ...

      Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
      "Science is about questioning the status quo. Questioning authority".
      In the absence of evidence, opinion is indistinguishable from prejudice.