pf2 0.4.0 → 0.5.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,242 @@
1
+ pub mod configuration;
2
+ mod new_thread_watcher;
3
+ pub mod ruby_object;
4
+
5
+ use std::collections::HashSet;
6
+ use std::ffi::{c_int, CStr, CString};
7
+ use std::str::FromStr as _;
8
+ use std::sync::atomic::{AtomicBool, Ordering};
9
+ use std::sync::{Arc, RwLock};
10
+ use std::thread;
11
+ use std::time::Duration;
12
+
13
+ use rb_sys::*;
14
+
15
+ use self::configuration::Configuration;
16
+ use self::new_thread_watcher::NewThreadWatcher;
17
+ use crate::profile::Profile;
18
+ use crate::scheduler::Scheduler;
19
+ #[cfg(target_os = "linux")]
20
+ use crate::signal_scheduler::SignalScheduler;
21
+ #[cfg(not(target_os = "linux"))]
22
+ use crate::signal_scheduler_unsupported_platform::SignalScheduler;
23
+ use crate::timer_thread_scheduler::TimerThreadScheduler;
24
+ use crate::util::*;
25
+
26
+ pub struct Session {
27
+ pub configuration: Configuration,
28
+ pub scheduler: Arc<dyn Scheduler>,
29
+ pub profile: Arc<RwLock<Profile>>,
30
+ pub running: Arc<AtomicBool>,
31
+ pub new_thread_watcher: Option<NewThreadWatcher>,
32
+ }
33
+
34
+ impl Session {
35
+ pub fn new_from_rb_initialize(argc: c_int, argv: *const VALUE, rbself: VALUE) -> Self {
36
+ // Parse arguments
37
+ let kwargs: VALUE = Qnil.into();
38
+ unsafe {
39
+ rb_scan_args(argc, argv, cstr!(":"), &kwargs);
40
+ };
41
+ let mut kwargs_values: [VALUE; 4] = [Qnil.into(); 4];
42
+ unsafe {
43
+ rb_get_kwargs(
44
+ kwargs,
45
+ [
46
+ rb_intern(cstr!("interval_ms")),
47
+ rb_intern(cstr!("threads")),
48
+ rb_intern(cstr!("time_mode")),
49
+ rb_intern(cstr!("scheduler")),
50
+ ]
51
+ .as_mut_ptr(),
52
+ 0,
53
+ 4,
54
+ kwargs_values.as_mut_ptr(),
55
+ );
56
+ };
57
+
58
+ let interval = Self::parse_option_interval_ms(kwargs_values[0]);
59
+ let threads = Self::parse_option_threads(kwargs_values[1]);
60
+ let time_mode = Self::parse_option_time_mode(kwargs_values[2]);
61
+ let scheduler = Self::parse_option_scheduler(kwargs_values[3]);
62
+
63
+ let configuration = Configuration {
64
+ scheduler,
65
+ interval,
66
+ target_ruby_threads: threads.clone(),
67
+ time_mode,
68
+ };
69
+
70
+ match configuration.validate() {
71
+ Ok(_) => {}
72
+ Err(msg) => unsafe {
73
+ rb_raise(rb_eArgError, CString::new(msg).unwrap().as_c_str().as_ptr());
74
+ },
75
+ };
76
+
77
+ // Store configuration as a Ruby Hash for convenience
78
+ unsafe {
79
+ rb_iv_set(rbself, cstr!("@configuration"), configuration.to_rb_hash());
80
+ }
81
+
82
+ // Create a new Profile
83
+ let profile = Arc::new(RwLock::new(Profile::new()));
84
+
85
+ // Initialize the specified Scheduler
86
+ let scheduler: Arc<dyn Scheduler> = match configuration.scheduler {
87
+ configuration::Scheduler::Signal => {
88
+ Arc::new(SignalScheduler::new(&configuration, Arc::clone(&profile)))
89
+ }
90
+ configuration::Scheduler::TimerThread => Arc::new(TimerThreadScheduler::new(
91
+ &configuration,
92
+ Arc::clone(&profile),
93
+ )),
94
+ };
95
+
96
+ let new_thread_watcher = match threads {
97
+ configuration::Threads::All => {
98
+ let scheduler = Arc::clone(&scheduler);
99
+ Some(NewThreadWatcher::watch(move |thread: VALUE| {
100
+ log::debug!("New Ruby thread detected: {:?}", thread);
101
+ scheduler.on_new_thread(thread);
102
+ }))
103
+ }
104
+ configuration::Threads::Targeted(_) => None,
105
+ };
106
+
107
+ Session {
108
+ configuration,
109
+ scheduler,
110
+ profile,
111
+ running: Arc::new(AtomicBool::new(false)),
112
+ new_thread_watcher,
113
+ }
114
+ }
115
+
116
+ fn parse_option_interval_ms(value: VALUE) -> Duration {
117
+ if value == Qundef as VALUE {
118
+ // Return default
119
+ return configuration::DEFAULT_INTERVAL;
120
+ }
121
+
122
+ let interval_ms = unsafe { rb_num2long(value) };
123
+ Duration::from_millis(interval_ms.try_into().unwrap_or_else(|_| {
124
+ eprintln!(
125
+ "[Pf2] Warning: Specified interval ({}) is not valid. Using default value (49ms).",
126
+ interval_ms
127
+ );
128
+ 49
129
+ }))
130
+ }
131
+
132
+ fn parse_option_threads(value: VALUE) -> configuration::Threads {
133
+ if (value == Qundef as VALUE)
134
+ || (value == Qnil as VALUE)
135
+ || (value == unsafe { rb_id2sym(rb_intern(cstr!("all"))) })
136
+ {
137
+ return configuration::Threads::All;
138
+ }
139
+
140
+ let mut set: HashSet<VALUE> = HashSet::new();
141
+ unsafe {
142
+ for i in 0..RARRAY_LEN(value) {
143
+ set.insert(rb_ary_entry(value, i));
144
+ }
145
+ }
146
+ configuration::Threads::Targeted(set)
147
+ }
148
+
149
+ fn parse_option_time_mode(value: VALUE) -> configuration::TimeMode {
150
+ if value == Qundef as VALUE {
151
+ // Return default
152
+ return configuration::DEFAULT_TIME_MODE;
153
+ }
154
+
155
+ let specified_mode = unsafe {
156
+ let mut str = rb_funcall(value, rb_intern(cstr!("to_s")), 0);
157
+ let ptr = rb_string_value_ptr(&mut str);
158
+ CStr::from_ptr(ptr).to_str().unwrap()
159
+ };
160
+ configuration::TimeMode::from_str(specified_mode).unwrap_or_else(|_| {
161
+ // Raise an ArgumentError if the mode is invalid
162
+ unsafe {
163
+ rb_raise(
164
+ rb_eArgError,
165
+ cstr!("Invalid time mode. Valid values are 'cpu' and 'wall'."),
166
+ )
167
+ }
168
+ })
169
+ }
170
+
171
+ fn parse_option_scheduler(value: VALUE) -> configuration::Scheduler {
172
+ if value == Qundef as VALUE {
173
+ // Return default
174
+ return configuration::DEFAULT_SCHEDULER;
175
+ }
176
+
177
+ let specified_scheduler = unsafe {
178
+ let mut str = rb_funcall(value, rb_intern(cstr!("to_s")), 0);
179
+ let ptr = rb_string_value_ptr(&mut str);
180
+ CStr::from_ptr(ptr).to_str().unwrap()
181
+ };
182
+ let scheduler =
183
+ configuration::Scheduler::from_str(specified_scheduler).unwrap_or_else(|_| {
184
+ // Raise an ArgumentError if the mode is invalid
185
+ unsafe {
186
+ rb_raise(
187
+ rb_eArgError,
188
+ cstr!("Invalid scheduler. Valid values are ':signal' and ':timer_thread'."),
189
+ )
190
+ }
191
+ });
192
+
193
+ // Raise an ArgumentError if the scheduler is not supported on the current platform
194
+ if !cfg!(target_os = "linux") && scheduler == configuration::Scheduler::Signal {
195
+ unsafe {
196
+ rb_raise(
197
+ rb_eArgError,
198
+ cstr!("Signal scheduler is not supported on this platform."),
199
+ )
200
+ }
201
+ }
202
+ scheduler
203
+ }
204
+
205
+ pub fn start(&mut self) -> VALUE {
206
+ self.running.store(true, Ordering::Relaxed);
207
+ self.start_profile_buffer_flusher_thread();
208
+ self.scheduler.start()
209
+ }
210
+
211
+ fn start_profile_buffer_flusher_thread(&self) {
212
+ let profile = Arc::clone(&self.profile);
213
+ let running = Arc::clone(&self.running);
214
+ log::debug!("flusher: Starting");
215
+ thread::spawn(move || loop {
216
+ if !running.load(Ordering::Relaxed) {
217
+ log::debug!("flusher: Exiting");
218
+ break;
219
+ }
220
+
221
+ log::trace!("flusher: Flushing temporary sample buffer");
222
+ match profile.try_write() {
223
+ Ok(mut profile) => {
224
+ profile.flush_temporary_sample_buffer();
225
+ }
226
+ Err(_) => {
227
+ log::debug!("flusher: Failed to acquire profile lock");
228
+ }
229
+ }
230
+ thread::sleep(Duration::from_millis(500));
231
+ });
232
+ }
233
+
234
+ pub fn stop(&mut self) -> VALUE {
235
+ self.running.store(false, Ordering::Relaxed);
236
+ self.scheduler.stop()
237
+ }
238
+
239
+ pub fn dmark(&self) {
240
+ self.scheduler.dmark()
241
+ }
242
+ }
@@ -1,22 +1,16 @@
1
1
  #![deny(unsafe_op_in_unsafe_fn)]
2
2
 
3
- mod configuration;
4
- mod timer_installer;
5
-
6
- use self::configuration::{Configuration, TimeMode};
7
- use self::timer_installer::TimerInstaller;
8
3
  use crate::profile::Profile;
9
4
  use crate::profile_serializer::ProfileSerializer;
5
+ use crate::ruby_internal_apis::rb_thread_getcpuclockid;
10
6
  use crate::sample::Sample;
7
+ use crate::scheduler::Scheduler;
8
+ use crate::session::configuration::{self, Configuration};
11
9
 
12
10
  use core::panic;
13
- use std::collections::HashSet;
14
- use std::ffi::{c_int, c_void, CStr, CString};
11
+ use std::ffi::{c_int, c_void, CString};
15
12
  use std::mem::ManuallyDrop;
16
- use std::str::FromStr;
17
13
  use std::sync::{Arc, RwLock};
18
- use std::thread;
19
- use std::time::Duration;
20
14
  use std::{mem, ptr::null_mut};
21
15
 
22
16
  use rb_sys::*;
@@ -25,8 +19,8 @@ use crate::util::*;
25
19
 
26
20
  #[derive(Debug)]
27
21
  pub struct SignalScheduler {
28
- configuration: Option<configuration::Configuration>,
29
- profile: Option<Arc<RwLock<Profile>>>,
22
+ configuration: Configuration,
23
+ profile: Arc<RwLock<Profile>>,
30
24
  }
31
25
 
32
26
  pub struct SignalHandlerArgs {
@@ -34,131 +28,69 @@ pub struct SignalHandlerArgs {
34
28
  context_ruby_thread: VALUE,
35
29
  }
36
30
 
37
- impl SignalScheduler {
38
- fn new() -> Self {
39
- Self {
40
- configuration: None,
41
- profile: None,
42
- }
43
- }
44
-
45
- fn initialize(&mut self, argc: c_int, argv: *const VALUE, _rbself: VALUE) -> VALUE {
46
- // Parse arguments
47
- let kwargs: VALUE = Qnil.into();
48
- unsafe {
49
- rb_scan_args(argc, argv, cstr!(":"), &kwargs);
50
- };
51
- let mut kwargs_values: [VALUE; 4] = [Qnil.into(); 4];
52
- unsafe {
53
- rb_get_kwargs(
54
- kwargs,
55
- [
56
- rb_intern(cstr!("interval_ms")),
57
- rb_intern(cstr!("threads")),
58
- rb_intern(cstr!("time_mode")),
59
- rb_intern(cstr!("track_all_threads")),
60
- ]
61
- .as_mut_ptr(),
62
- 0,
63
- 4,
64
- kwargs_values.as_mut_ptr(),
65
- );
66
- };
67
- let interval: Duration = if kwargs_values[0] != Qundef as VALUE {
68
- let interval_ms = unsafe { rb_num2long(kwargs_values[0]) };
69
- Duration::from_millis(interval_ms.try_into().unwrap_or_else(|_| {
70
- eprintln!(
71
- "[Pf2] Warning: Specified interval ({}) is not valid. Using default value (49ms).",
72
- interval_ms
73
- );
74
- 49
75
- }))
76
- } else {
77
- Duration::from_millis(49)
78
- };
79
- let threads: VALUE = if kwargs_values[1] != Qundef as VALUE {
80
- kwargs_values[1]
81
- } else {
82
- unsafe { rb_funcall(rb_cThread, rb_intern(cstr!("list")), 0) }
83
- };
84
- let time_mode: configuration::TimeMode = if kwargs_values[2] != Qundef as VALUE {
85
- let specified_mode = unsafe {
86
- let mut str = rb_funcall(kwargs_values[2], rb_intern(cstr!("to_s")), 0);
87
- let ptr = rb_string_value_ptr(&mut str);
88
- CStr::from_ptr(ptr).to_str().unwrap()
89
- };
90
- configuration::TimeMode::from_str(specified_mode).unwrap_or_else(|_| {
91
- // Raise an ArgumentError
92
- unsafe {
93
- rb_raise(
94
- rb_eArgError,
95
- cstr!("Invalid time mode. Valid values are 'cpu' and 'wall'."),
96
- )
97
- }
98
- })
99
- } else {
100
- configuration::TimeMode::CpuTime
101
- };
102
- let track_all_threads: bool = if kwargs_values[3] != Qundef as VALUE {
103
- RTEST(kwargs_values[3])
104
- } else {
105
- false
106
- };
31
+ impl Scheduler for SignalScheduler {
32
+ fn start(&self) -> VALUE {
33
+ self.install_signal_handler();
107
34
 
108
- let mut target_ruby_threads = HashSet::new();
109
- unsafe {
110
- for i in 0..RARRAY_LEN(threads) {
111
- let ruby_thread: VALUE = rb_ary_entry(threads, i);
112
- target_ruby_threads.insert(ruby_thread);
35
+ if let configuration::Threads::Targeted(threads) = &self.configuration.target_ruby_threads {
36
+ for ruby_thread in threads.iter() {
37
+ self.install_timer_to_ruby_thread(*ruby_thread);
113
38
  }
114
39
  }
115
40
 
116
- self.configuration = Some(Configuration {
117
- interval,
118
- target_ruby_threads,
119
- time_mode,
120
- track_all_threads,
121
- });
122
-
123
- Qnil.into()
41
+ Qtrue.into()
124
42
  }
125
43
 
126
- fn start(&mut self, _rbself: VALUE) -> VALUE {
127
- let profile = Arc::new(RwLock::new(Profile::new()));
128
- self.start_profile_buffer_flusher_thread(&profile);
129
- self.install_signal_handler();
44
+ fn stop(&self) -> VALUE {
45
+ // Finalize
46
+ match self.profile.try_write() {
47
+ Ok(mut profile) => {
48
+ profile.flush_temporary_sample_buffer();
49
+ }
50
+ Err(_) => {
51
+ println!("[pf2 ERROR] stop: Failed to acquire profile lock.");
52
+ return Qfalse.into();
53
+ }
54
+ }
130
55
 
131
- TimerInstaller::install_timer_to_ruby_threads(
132
- self.configuration.as_ref().unwrap().clone(), // FIXME: don't clone
133
- Arc::clone(&profile),
134
- );
56
+ let profile = self.profile.try_read().unwrap();
57
+ log::debug!("Number of samples: {}", profile.samples.len());
135
58
 
136
- self.profile = Some(profile);
59
+ let serialized = ProfileSerializer::serialize(&profile);
60
+ let serialized = CString::new(serialized).unwrap();
61
+ unsafe { rb_str_new_cstr(serialized.as_ptr()) }
62
+ }
137
63
 
138
- Qtrue.into()
64
+ fn on_new_thread(&self, thread: VALUE) {
65
+ self.install_timer_to_ruby_thread(thread);
139
66
  }
140
67
 
141
- fn stop(&mut self, _rbself: VALUE) -> VALUE {
142
- if let Some(profile) = &self.profile {
143
- // Finalize
144
- match profile.try_write() {
145
- Ok(mut profile) => {
146
- profile.flush_temporary_sample_buffer();
147
- }
148
- Err(_) => {
149
- println!("[pf2 ERROR] stop: Failed to acquire profile lock.");
150
- return Qfalse.into();
151
- }
68
+ fn dmark(&self) {
69
+ match self.profile.read() {
70
+ Ok(profile) => unsafe {
71
+ profile.dmark();
72
+ },
73
+ Err(_) => {
74
+ panic!("[pf2 FATAL] dmark: Failed to acquire profile lock.");
152
75
  }
76
+ }
77
+ }
153
78
 
154
- let profile = profile.try_read().unwrap();
155
- log::debug!("Number of samples: {}", profile.samples.len());
79
+ fn dfree(&self) {
80
+ // No-op
81
+ }
156
82
 
157
- let serialized = ProfileSerializer::serialize(&profile);
158
- let serialized = CString::new(serialized).unwrap();
159
- unsafe { rb_str_new_cstr(serialized.as_ptr()) }
160
- } else {
161
- panic!("stop() called before start()");
83
+ fn dsize(&self) -> size_t {
84
+ // FIXME: Report something better
85
+ mem::size_of::<Self>() as size_t
86
+ }
87
+ }
88
+
89
+ impl SignalScheduler {
90
+ pub fn new(configuration: &Configuration, profile: Arc<RwLock<Profile>>) -> Self {
91
+ Self {
92
+ configuration: configuration.clone(),
93
+ profile,
162
94
  }
163
95
  }
164
96
 
@@ -202,110 +134,62 @@ impl SignalScheduler {
202
134
  }
203
135
  }
204
136
 
205
- fn start_profile_buffer_flusher_thread(&self, profile: &Arc<RwLock<Profile>>) {
206
- let profile = Arc::clone(profile);
207
- thread::spawn(move || loop {
208
- log::trace!("Flushing temporary sample buffer");
209
- match profile.try_write() {
210
- Ok(mut profile) => {
211
- profile.flush_temporary_sample_buffer();
212
- }
213
- Err(_) => {
214
- log::debug!("flusher: Failed to acquire profile lock");
215
- }
216
- }
217
- thread::sleep(Duration::from_millis(500));
137
+ fn install_timer_to_ruby_thread(&self, ruby_thread: VALUE) {
138
+ // NOTE: This Box never gets dropped
139
+ let signal_handler_args = Box::new(SignalHandlerArgs {
140
+ profile: Arc::clone(&self.profile),
141
+ context_ruby_thread: ruby_thread,
218
142
  });
219
- }
220
-
221
- // Ruby Methods
222
-
223
- pub unsafe extern "C" fn rb_initialize(
224
- argc: c_int,
225
- argv: *const VALUE,
226
- rbself: VALUE,
227
- ) -> VALUE {
228
- let mut collector = unsafe { Self::get_struct_from(rbself) };
229
- collector.initialize(argc, argv, rbself)
230
- }
231
-
232
- pub unsafe extern "C" fn rb_start(rbself: VALUE) -> VALUE {
233
- let mut collector = unsafe { Self::get_struct_from(rbself) };
234
- collector.start(rbself)
235
- }
236
-
237
- pub unsafe extern "C" fn rb_stop(rbself: VALUE) -> VALUE {
238
- let mut collector = unsafe { Self::get_struct_from(rbself) };
239
- collector.stop(rbself)
240
- }
241
-
242
- // Functions for TypedData
243
-
244
- // Extract the SignalScheduler struct from a Ruby object
245
- unsafe fn get_struct_from(obj: VALUE) -> ManuallyDrop<Box<Self>> {
246
- unsafe {
247
- let ptr = rb_check_typeddata(obj, &RBDATA);
248
- ManuallyDrop::new(Box::from_raw(ptr as *mut SignalScheduler))
249
- }
250
- }
251
-
252
- #[allow(non_snake_case)]
253
- pub unsafe extern "C" fn rb_alloc(_rbself: VALUE) -> VALUE {
254
- let collector = Box::new(SignalScheduler::new());
255
- unsafe { Arc::increment_strong_count(&collector) };
256
-
257
- unsafe {
258
- let rb_mPf2: VALUE = rb_define_module(cstr!("Pf2"));
259
- let rb_cSignalScheduler =
260
- rb_define_class_under(rb_mPf2, cstr!("SignalScheduler"), rb_cObject);
261
143
 
262
- // "Wrap" the SignalScheduler struct into a Ruby object
263
- rb_data_typed_object_wrap(
264
- rb_cSignalScheduler,
265
- Box::into_raw(collector) as *mut c_void,
266
- &RBDATA,
267
- )
144
+ // rb_funcall deadlocks when called within a THREAD_EVENT_STARTED hook
145
+ let kernel_thread_id: i32 = i32::try_from(unsafe {
146
+ rb_num2int(rb_funcall(
147
+ ruby_thread,
148
+ rb_intern(cstr!("native_thread_id")), // kernel thread ID
149
+ 0,
150
+ ))
151
+ })
152
+ .unwrap();
153
+
154
+ // Create a signal event
155
+ let mut sigevent: libc::sigevent = unsafe { mem::zeroed() };
156
+ // Note: SIGEV_THREAD_ID is Linux-specific. In other platforms, we would need to
157
+ // "trampoline" the signal as any pthread can receive the signal.
158
+ sigevent.sigev_notify = libc::SIGEV_THREAD_ID;
159
+ sigevent.sigev_notify_thread_id = kernel_thread_id;
160
+ sigevent.sigev_signo = libc::SIGALRM;
161
+ // Pass required args to the signal handler
162
+ sigevent.sigev_value.sival_ptr = Box::into_raw(signal_handler_args) as *mut c_void;
163
+
164
+ // Create and configure timer to fire every _interval_ ms of CPU time
165
+ let mut timer: libc::timer_t = unsafe { mem::zeroed() };
166
+ let clockid = match self.configuration.time_mode {
167
+ configuration::TimeMode::CpuTime => unsafe { rb_thread_getcpuclockid(ruby_thread) },
168
+ configuration::TimeMode::WallTime => libc::CLOCK_MONOTONIC,
169
+ };
170
+ let err = unsafe { libc::timer_create(clockid, &mut sigevent, &mut timer) };
171
+ if err != 0 {
172
+ panic!("timer_create failed: {}", err);
268
173
  }
269
- }
270
-
271
- unsafe extern "C" fn dmark(ptr: *mut c_void) {
272
- unsafe {
273
- let collector = ManuallyDrop::new(Box::from_raw(ptr as *mut SignalScheduler));
274
- if let Some(profile) = &collector.profile {
275
- match profile.read() {
276
- Ok(profile) => {
277
- profile.dmark();
278
- }
279
- Err(_) => {
280
- panic!("[pf2 FATAL] dmark: Failed to acquire profile lock.");
281
- }
282
- }
283
- }
174
+ let itimerspec = Self::duration_to_itimerspec(&self.configuration.interval);
175
+ let err = unsafe { libc::timer_settime(timer, 0, &itimerspec, null_mut()) };
176
+ if err != 0 {
177
+ panic!("timer_settime failed: {}", err);
284
178
  }
285
- }
286
179
 
287
- unsafe extern "C" fn dfree(ptr: *mut c_void) {
288
- unsafe {
289
- drop(Box::from_raw(ptr as *mut SignalScheduler));
290
- }
180
+ log::debug!("timer registered for thread {}", ruby_thread);
291
181
  }
292
182
 
293
- unsafe extern "C" fn dsize(_: *const c_void) -> size_t {
294
- // FIXME: Report something better
295
- mem::size_of::<SignalScheduler>() as size_t
183
+ fn duration_to_itimerspec(duration: &std::time::Duration) -> libc::itimerspec {
184
+ let nanos = duration.as_nanos();
185
+ let seconds_part: i64 = (nanos / 1_000_000_000).try_into().unwrap();
186
+ let nanos_part: i64 = (nanos % 1_000_000_000).try_into().unwrap();
187
+
188
+ let mut its: libc::itimerspec = unsafe { mem::zeroed() };
189
+ its.it_interval.tv_sec = seconds_part;
190
+ its.it_interval.tv_nsec = nanos_part;
191
+ its.it_value.tv_sec = seconds_part;
192
+ its.it_value.tv_nsec = nanos_part;
193
+ its
296
194
  }
297
195
  }
298
-
299
- static mut RBDATA: rb_data_type_t = rb_data_type_t {
300
- wrap_struct_name: cstr!("SignalScheduler"),
301
- function: rb_data_type_struct__bindgen_ty_1 {
302
- dmark: Some(SignalScheduler::dmark),
303
- dfree: Some(SignalScheduler::dfree),
304
- dsize: Some(SignalScheduler::dsize),
305
- dcompact: None,
306
- reserved: [null_mut(); 1],
307
- },
308
- parent: null_mut(),
309
- data: null_mut(),
310
- flags: 0,
311
- };
@@ -0,0 +1,39 @@
1
+ use std::sync::{Arc, RwLock};
2
+
3
+ use crate::profile::Profile;
4
+ use crate::scheduler::Scheduler;
5
+ use crate::session::configuration::Configuration;
6
+
7
+ pub struct SignalScheduler {}
8
+
9
+ impl Scheduler for SignalScheduler {
10
+ fn start(&self) -> rb_sys::VALUE {
11
+ unimplemented!()
12
+ }
13
+
14
+ fn stop(&self) -> rb_sys::VALUE {
15
+ unimplemented!()
16
+ }
17
+
18
+ fn on_new_thread(&self, thread: rb_sys::VALUE) {
19
+ unimplemented!()
20
+ }
21
+
22
+ fn dmark(&self) {
23
+ unimplemented!()
24
+ }
25
+
26
+ fn dfree(&self) {
27
+ unimplemented!()
28
+ }
29
+
30
+ fn dsize(&self) -> rb_sys::size_t {
31
+ unimplemented!()
32
+ }
33
+ }
34
+
35
+ impl SignalScheduler {
36
+ pub fn new(configuration: &Configuration, profile: Arc<RwLock<Profile>>) -> Self {
37
+ unimplemented!()
38
+ }
39
+ }