1- use crate :: { PyObjectRef , PyResult , VirtualMachine } ;
21use core:: {
32 cell:: { Cell , RefCell } ,
43 fmt,
5- ops:: { Deref , DerefMut } ,
4+ ops:: { Deref , DerefMut , Index , IndexMut , Range } ,
65 sync:: atomic:: { AtomicBool , Ordering } ,
76} ;
87use std:: sync:: mpsc;
98
109#[ cfg( windows) ]
1110use core:: sync:: atomic:: AtomicIsize ;
1211
13- static ANY_TRIGGERED : AtomicBool = AtomicBool :: new ( false ) ;
12+ use crate :: { PyObjectRef , PyResult , TryFromBorrowedObject , TryFromObject , VirtualMachine } ;
1413
1514pub ( crate ) const NSIG : usize = 64 ;
1615
16+ static ANY_TRIGGERED : AtomicBool = AtomicBool :: new ( false ) ;
17+
1718#[ expect(
1819 clippy:: declare_interior_mutable_const,
1920 reason = "workaround for const array repeat limitation (rust issue #79270)"
@@ -70,22 +71,32 @@ fn trigger_signals(vm: &VirtualMachine) -> PyResult<()> {
7071 }
7172 let _guard = SignalHandlerGuard ;
7273
73- // unwrap should never fail since we check above
74- let signal_handlers = vm. signal_handlers . get ( ) . unwrap ( ) . borrow ( ) ;
74+ let signal_handlers = vm
75+ . signal_handlers
76+ . get ( )
77+ . expect ( "should never fail since we check above" )
78+ . borrow ( ) ;
79+
7580 for ( signum, trigger) in TRIGGERS . iter ( ) . enumerate ( ) . skip ( 1 ) {
7681 let triggered = trigger. swap ( false , Ordering :: Relaxed ) ;
82+
83+ // SAFETY: TRIGGERS has the same length as the signal_handlers
84+ let signum = unsafe { SignalNum :: new_unchecked ( signum as i32 ) } ;
85+
7786 if triggered
7887 && let Some ( handler) = & signal_handlers[ signum]
7988 && let Some ( callable) = handler. to_callable ( )
8089 {
81- callable. invoke ( ( signum, vm. ctx . none ( ) ) , vm) ?;
90+ callable. invoke ( ( signum. as_i32 ( ) , vm. ctx . none ( ) ) , vm) ?;
8291 }
8392 }
93+
8494 if let Some ( signal_rx) = & vm. signal_rx {
8595 for f in signal_rx. rx . try_iter ( ) {
8696 f ( vm) ?;
8797 }
8898 }
99+
89100 Ok ( ( ) )
90101}
91102
@@ -109,28 +120,94 @@ pub(crate) fn clear_after_fork() {
109120 }
110121}
111122
112- pub fn assert_in_range ( signum : i32 , vm : & VirtualMachine ) -> PyResult < ( ) > {
113- if ( 1 ..NSIG as i32 ) . contains ( & signum) {
114- Ok ( ( ) )
115- } else {
116- Err ( vm. new_value_error ( "signal number out of range" ) )
123+ /// A valid signal number.
124+ #[ derive( Clone , Copy , Debug , Eq , PartialEq , PartialOrd , Ord ) ]
125+ pub struct SignalNum ( i32 ) ;
126+
127+ impl SignalNum {
128+ pub ( crate ) const VALID_RANGE : Range < i32 > = 1 ..NSIG as i32 ;
129+
130+ /// Alias for:
131+ /// ```rust
132+ /// # use rustpython_vm::signal::SignalNum;
133+ ///
134+ /// unsafe { SignalNum::new_unchecked(libc::SIGINT) };
135+ /// ```
136+ #[ cfg( any( unix, windows) ) ]
137+ #[ allow( dead_code, reason = "Not used on all platforms" ) ]
138+ pub ( crate ) const SIGINT : Self = Self ( libc:: SIGINT ) ;
139+
140+ /// Construct [`Self`] without any validation on the signalnum value.
141+ ///
142+ /// # Safety
143+ ///
144+ /// Caller's responsibility to ensure the signal num is valid.
145+ #[ must_use]
146+ pub const unsafe fn new_unchecked ( value : i32 ) -> Self {
147+ Self ( value)
148+ }
149+
150+ /// Get the self as an [`i32`].
151+ #[ must_use]
152+ pub const fn as_i32 ( & self ) -> i32 {
153+ self . 0
154+ }
155+
156+ /// Get the self as an [`usize`].
157+ #[ must_use]
158+ pub const fn as_usize ( & self ) -> usize {
159+ self . 0 as usize
160+ }
161+ }
162+
163+ impl fmt:: Display for SignalNum {
164+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
165+ fmt:: Display :: fmt ( & self . 0 , f)
166+ }
167+ }
168+
169+ impl From < SignalNum > for i32 {
170+ fn from ( signalnum : SignalNum ) -> Self {
171+ signalnum. as_i32 ( )
172+ }
173+ }
174+
175+ impl TryFrom < i32 > for SignalNum {
176+ type Error = String ;
177+
178+ fn try_from ( value : i32 ) -> Result < Self , Self :: Error > {
179+ let bounds = cfg_select ! {
180+ all( windows, feature = "host_env" ) => rustpython_host_env:: signal:: VALID_SIGNALS ,
181+ _ => Self :: VALID_RANGE ,
182+ } ;
183+
184+ if bounds. contains ( & value) {
185+ Ok ( Self ( value) )
186+ } else {
187+ Err ( "signal number out of range" . into ( ) )
188+ }
189+ }
190+ }
191+
192+ impl TryFromObject for SignalNum {
193+ fn try_from_object ( vm : & VirtualMachine , obj : PyObjectRef ) -> PyResult < Self > {
194+ Self :: try_from ( i32:: try_from_borrowed_object ( vm, & obj) ?)
195+ . map_err ( |msg| vm. new_value_error ( msg) )
117196 }
118197}
119198
120199/// Similar to `PyErr_SetInterruptEx` in CPython
121200///
122201/// Missing signal handler for the given signal number is silently ignored.
123- #[ allow( dead_code) ]
124202#[ cfg( all( not( target_arch = "wasm32" ) , feature = "host_env" ) ) ]
125- pub fn set_interrupt_ex ( signum : i32 , vm : & VirtualMachine ) -> PyResult < ( ) > {
203+ pub fn set_interrupt_ex ( signum : SignalNum ) -> PyResult < ( ) > {
126204 use crate :: stdlib:: _signal:: _signal:: { SIG_DFL , SIG_IGN , run_signal} ;
127- assert_in_range ( signum, vm) ?;
128205
129- match signum as usize {
206+ match signum. as_usize ( ) {
130207 SIG_DFL | SIG_IGN => Ok ( ( ) ) ,
131208 _ => {
132209 // interrupt the main thread with given signal number
133- run_signal ( signum) ;
210+ run_signal ( signum. into ( ) ) ;
134211 Ok ( ( ) )
135212 }
136213 }
@@ -190,16 +267,38 @@ pub fn get_sigint_event() -> Option<isize> {
190267 if handle == 0 { None } else { Some ( handle) }
191268}
192269
193- pub struct SignalHandlers ( Box < RefCell < [ Option < PyObjectRef > ; NSIG ] > > ) ;
270+ pub struct SignalHandlersInner ( [ Option < PyObjectRef > ; NSIG ] ) ;
271+
272+ impl Default for SignalHandlersInner {
273+ fn default ( ) -> Self {
274+ Self ( [ const { None } ; NSIG ] )
275+ }
276+ }
277+
278+ impl Index < SignalNum > for SignalHandlersInner {
279+ type Output = Option < PyObjectRef > ;
280+
281+ fn index ( & self , index : SignalNum ) -> & Self :: Output {
282+ & self . 0 [ index. as_usize ( ) ]
283+ }
284+ }
285+
286+ impl IndexMut < SignalNum > for SignalHandlersInner {
287+ fn index_mut ( & mut self , index : SignalNum ) -> & mut Self :: Output {
288+ & mut self . 0 [ index. as_usize ( ) ]
289+ }
290+ }
291+
292+ pub struct SignalHandlers ( Box < RefCell < SignalHandlersInner > > ) ;
194293
195294impl Default for SignalHandlers {
196295 fn default ( ) -> Self {
197- Self ( Box :: new ( const { RefCell :: new ( [ const { None } ; NSIG ] ) } ) )
296+ Self ( Box :: new ( RefCell :: new ( SignalHandlersInner :: default ( ) ) ) )
198297 }
199298}
200299
201300impl Deref for SignalHandlers {
202- type Target = Box < RefCell < [ Option < PyObjectRef > ; NSIG ] > > ;
301+ type Target = Box < RefCell < SignalHandlersInner > > ;
203302
204303 fn deref ( & self ) -> & Self :: Target {
205304 & self . 0
0 commit comments