user2842165 user2842165 - 7 months ago 23
Perl Question

Change thread priority ERROR_INVALID_HANDLE

I'm trying to change a thread priority within my script, without success, here are the details.

$thr = threads->new(\&someFunction,
$shared variable 1,
$shared variable 2,
);


I've tried using
threads::State
;

$thr->priority(2);


Without success

So, I thought the
Win32::API
must work

my $functionGetLastError= Win32::API->new('Kernel32',
'GetLastError',
'',
'N'
);
my $functionSetThreadPriority= Win32::API->new('Kernel32',
'SetThreadPriority',
'II', # I've tried 'PI' and 'II' as well
'N'
);
my $h = $thr->_handle();
my $success = $functionSetThreadPriority->Call( $h, 2 );
warn "Return Error #".$functionGetLastError->Call() if !$success;


Again, without success: (, but now I have a clue, the script return error number


last Error 6


From MSDN site, System Error Codes (0-499), it seems that the error is


ERROR_INVALID_HANDLE


What am I doing wrong?

Answer

$thread->_handle weirdly returns a HANDLE*, while SetThreadPriority expects a HANDLE. You need to dereference the pointer, which you can do as follows:

use constant THREAD_PRIORITY_HIGHEST => 2;

sub SetThreadPriority {
   my ($thread, $priority) = @_;

   # $thread->_handle() returns a HANDLE*.
   my $handle_ptr    = $thread->_handle();
   my $packed_handle = unpack('P'.HANDLE_SIZE, pack(PTR_FORMAT, $handle_ptr));
   my $handle        = unpack(HANDLE_FORMAT, $packed_handle);

   state $SetThreadPriority = (
      Win32::API->new('Kernel32', 'SetThreadPriority', 'Ni', 'i')
         or die("Loading SetThreadPriority: $^E\n")
   );

   return $SetThreadPriority->Call($handle, $priority);
}

Here's the full test program:

use strict;
use warnings;
use feature qw( say state );

use threads;
use threads::shared;

use Carp       qw( croak );
use Config     qw( %Config );
use Win32::API qw( );

sub uint_format {
     $_[0] == 4 ? 'L'
   : $_[0] == 8 ? 'Q'
   : croak("Unsupported")
}

use constant PTR_SIZE   => $Config{ptrsize};
use constant PTR_FORMAT => uint_format(PTR_SIZE);

use constant HANDLE_SIZE   => PTR_SIZE;
use constant HANDLE_FORMAT => PTR_FORMAT;

use constant THREAD_PRIORITY_HIGHEST => 2;

sub SetThreadPriority {
   my ($thread, $priority) = @_;

   # $thread->_handle() returns a HANDLE*.
   my $handle_ptr    = $thread->_handle();
   my $packed_handle = unpack('P'.HANDLE_SIZE, pack(PTR_FORMAT, $handle_ptr));
   my $handle        = unpack(HANDLE_FORMAT, $packed_handle);

   state $SetThreadPriority = (
      Win32::API->new('Kernel32', 'SetThreadPriority', 'Ni', 'i')
         or die("Loading SetThreadPriority: $^E\n")
   );

   return $SetThreadPriority->Call($handle, $priority);
}

{
   my $done :shared = 0;

   my $thread = async {
      { lock($done); cond_wait($done) while !$done; }
   };

   my $rv = SetThreadPriority($thread, THREAD_PRIORITY_HIGHEST);
   say $rv ? "Success" : "Error: $^E";

   { lock($done); $done = 1; cond_broadcast($done); }
   $thread->join();
}

Notice that you can use $^E to access GetLastError.

SetThreadPriority($handle, THREAD_PRIORITY_HIGHEST)
   or die("SetThreadPriority: $^E\n";