Search code examples
multithreadingperlwinapithread-priority

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?


Solution

  • $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";