@@ -1549,6 +1549,61 @@ fn reconcile_play_state(world: &mut World) {
15491549 }
15501550}
15511551
1552+ #[ cfg( target_os = "windows" ) ]
1553+ mod pie_windows {
1554+ use std:: {
1555+ os:: {
1556+ raw:: c_void,
1557+ windows:: io:: { AsRawHandle , FromRawHandle , OwnedHandle } ,
1558+ } ,
1559+ process:: Child ,
1560+ sync:: LazyLock ,
1561+ } ;
1562+ use windows:: Win32 :: Foundation :: HANDLE ;
1563+ use windows:: Win32 :: System :: JobObjects :: * ;
1564+ use windows:: core:: PCWSTR ;
1565+
1566+ pub static PIE_JOB : LazyLock < Job > = LazyLock :: new ( || unsafe {
1567+ let handle = OwnedHandle :: from_raw_handle (
1568+ CreateJobObjectW ( None , PCWSTR :: null ( ) )
1569+ . expect ( "Failed to create job object for PIE" )
1570+ . 0 ,
1571+ ) ;
1572+
1573+ let info = JOBOBJECT_EXTENDED_LIMIT_INFORMATION {
1574+ BasicLimitInformation : JOBOBJECT_BASIC_LIMIT_INFORMATION {
1575+ LimitFlags : JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE
1576+ | JOB_OBJECT_LIMIT_BREAKAWAY_OK
1577+ | JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK ,
1578+ ..Default :: default ( )
1579+ } ,
1580+ ..Default :: default ( )
1581+ } ;
1582+
1583+ SetInformationJobObject (
1584+ HANDLE ( handle. as_raw_handle ( ) ) ,
1585+ JobObjectExtendedLimitInformation ,
1586+ & raw const info as * const c_void ,
1587+ std:: mem:: size_of :: < JOBOBJECT_EXTENDED_LIMIT_INFORMATION > ( ) as u32 ,
1588+ )
1589+ . expect ( "Failed to set job object info for PIE" ) ;
1590+
1591+ Job ( handle)
1592+ } ) ;
1593+
1594+ pub struct Job ( pub OwnedHandle ) ;
1595+
1596+ impl Job {
1597+ pub fn assign_to_process ( & self , process : & Child ) {
1598+ let handle = process. as_raw_handle ( ) ;
1599+ unsafe {
1600+ AssignProcessToJobObject ( HANDLE ( self . 0 . as_raw_handle ( ) ) , HANDLE ( handle) )
1601+ . expect ( "Failed to assign process to job for PIE" )
1602+ } ;
1603+ }
1604+ }
1605+ }
1606+
15521607/// Launch the project's game binary for one instance, point it at a
15531608/// fresh rendezvous, start draining its stderr, and begin awaiting its
15541609/// connection on a task pool. Returns the `Connecting` stage; on
@@ -1641,6 +1696,11 @@ fn spawn_instance(
16411696 }
16421697 } ;
16431698
1699+ // On Windows, we use a job object with JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE set.
1700+ // Because we own the only handle to the object, if Jackdaw crashes, the job object will be closed and the game killed.
1701+ #[ cfg( target_os = "windows" ) ]
1702+ pie_windows:: PIE_JOB . assign_to_process ( & child) ;
1703+
16441704 let stderr_tail: StderrTail = Arc :: new ( Mutex :: new ( VecDeque :: with_capacity ( STDERR_TAIL_LINES ) ) ) ;
16451705 if let Some ( stderr) = child. stderr . take ( ) {
16461706 let tail = Arc :: clone ( & stderr_tail) ;
0 commit comments