Skip to content

PyCall on thread != main: process will not exit #186

Description

@snickell

If you use PyCall from only one thread , but that thread is NOT the main thread, the process will not exit when the main thread exits.

This is not the same issue as: "Is PyCall Thread Safe" #96, as we are only using PyCall from one thread:

  1. PyCall is only called from one thread: "side_thread"
  2. side_thread exits
  3. Process still does not exit

Example:

#!/usr/bin/env ruby

def run(do_pycall_import:)
  if do_pycall_import
    puts "Running with do_pycall_import=true, process will not exit when main thread is done"
  else
    puts "Running with do_pycall_import=false, process will exit when main thread is done"
  end

  puts "Main thread ID: #{Thread.current.object_id}\n"

  def print_threads
    puts
    puts "Threads:"
    Thread.list.each do |thread|
      puts "\tThread ID: #{thread.object_id}, Status: #{thread.status}, Name: #{thread.name}"
    end
    puts
  end

  Thread.new do
    Thread.current.name = "side_thread"
    Thread.current.abort_on_exception = true

    # Demonstrate that PyCall has not been used:
    raise "Only load pycall in this thread" if defined?(PyCall)

    require 'pycall'

    if do_pycall_import
      puts "side_thread: import sys"
      # This will initialize libpython, if this happens, the process wil not exit:
      PyCall.import_module('sys')
    end

    sleep 2
    puts "side_thread: exiting"
  end

  sleep 1
  print_threads() #=> Two threads: main and side_thread

  sleep 4
  print_threads() #=> One thread: main
end

if __FILE__ == $0
  run(do_pycall_import: true) #=> Process does NOT exit after printing "End of main thread"
  # run(do_pycall_import: false) #=> Process exits after printing "End of main thread"
end

at_exit { puts "at_exit called"}
puts "End of main thread"

Output when do_pycall_import: true

zsh >> ./pycall_hangs_main_thread.rb
Running with do_pycall_import=true, process will not exit when main thread is done
Main thread ID: 60
side_thread: import sys

Threads:
        Thread ID: 60, Status: run, Name: 
        Thread ID: 80, Status: sleep, Name: side_thread

side_thread: exiting

Threads:
        Thread ID: 60, Status: run, Name: 

End of main thread
at_exit called

#=> control never returns to the shell

Output when do_pycall_import: false

zsh >> ./pycall_hangs_main_thread.rb
Running with do_pycall_import=false, process will exit when main thread is done
Main thread ID: 60

Threads:
        Thread ID: 60, Status: run, Name: 
        Thread ID: 80, Status: sleep, Name: side_thread

side_thread: exiting

Threads:
        Thread ID: 60, Status: run, Name: 

End of main thread
at_exit called

zsh >> # notice, process exited

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions