@@ -31,6 +31,9 @@ use serde::Deserialize;
3131use std:: time:: SystemTime ;
3232use std:: { thread, time} ;
3333
34+ use std:: sync:: atomic:: { AtomicBool , Ordering } ;
35+ use std:: sync:: Arc ;
36+
3437use berserker:: machine:: apply;
3538use berserker:: script:: { ast:: Node , parser:: parse_instructions} ;
3639use berserker:: {
@@ -53,8 +56,9 @@ struct Args {
5356 flag_f : Option < String > ,
5457}
5558
56- fn run_script ( script_path : String ) -> Vec < Option < i32 > > {
59+ fn run_script ( script_path : String ) -> Vec < ( i32 , u64 ) > {
5760 let mut handles = vec ! [ ] ;
61+ info ! ( "Loading script: {:?}" , script_path) ;
5862
5963 let ast: Vec < Node > =
6064 parse_instructions ( & std:: fs:: read_to_string ( script_path) . unwrap ( ) )
@@ -72,8 +76,8 @@ fn run_script(script_path: String) -> Vec<Option<i32>> {
7276 } ;
7377
7478 for instr in m_instructions {
75- debug ! ( "INSTR {:?}" , instr) ;
76- thread :: spawn ( move || apply ( instr. clone ( ) ) ) ;
79+ //thread::spawn(move || apply( instr.clone()) );
80+ let _ = apply ( instr. clone ( ) ) ;
7781 }
7882 } ;
7983
@@ -96,14 +100,22 @@ fn run_script(script_path: String) -> Vec<Option<i32>> {
96100 . unwrap_or ( String :: from ( "0" ) )
97101 . parse ( )
98102 . unwrap ( ) ;
103+
104+ let duration: u64 = args
105+ . get ( "duration" )
106+ . cloned ( )
107+ . unwrap_or ( String :: from ( "0" ) )
108+ . parse ( )
109+ . unwrap ( ) ;
110+
99111 let h: Vec < _ > = ( 0 ..workers)
100112 . map ( |_| {
101113 let worker = new_script_worker ( node. clone ( ) ) ;
102114
103115 match fork ( ) {
104116 Ok ( Fork :: Parent ( child) ) => {
105117 info ! ( "Child {}" , child) ;
106- Some ( child)
118+ Some ( ( child, duration ) )
107119 }
108120 Ok ( Fork :: Child ) => {
109121 worker. run_payload ( ) . unwrap ( ) ;
@@ -120,10 +132,10 @@ fn run_script(script_path: String) -> Vec<Option<i32>> {
120132 handles. extend ( h) ;
121133 } ) ;
122134
123- handles
135+ handles. iter ( ) . filter_map ( |i| * i ) . collect ( )
124136}
125137
126- fn run_workload ( config : WorkloadConfig ) -> Vec < Option < i32 > > {
138+ fn run_workload ( config : WorkloadConfig ) -> Vec < ( i32 , u64 ) > {
127139 let mut lower = 1024 ;
128140 let mut upper = 1024 ;
129141
@@ -142,7 +154,7 @@ fn run_workload(config: WorkloadConfig) -> Vec<Option<i32>> {
142154 match fork ( ) {
143155 Ok ( Fork :: Parent ( child) ) => {
144156 info ! ( "Child {}" , child) ;
145- Some ( child)
157+ Some ( ( child, config . duration ) )
146158 }
147159 Ok ( Fork :: Child ) => {
148160 if config. per_core {
@@ -162,83 +174,101 @@ fn run_workload(config: WorkloadConfig) -> Vec<Option<i32>> {
162174 . collect ( ) ;
163175
164176 info ! ( "In total: {}" , upper) ;
165- handles
177+ handles. iter ( ) . filter_map ( |i| * i ) . collect ( )
166178}
167179
168180fn main ( ) {
169181 env_logger:: init ( ) ;
170182
183+ let terminating = Arc :: new ( AtomicBool :: new ( false ) ) ;
184+ signal_hook:: flag:: register (
185+ signal_hook:: consts:: SIGTERM ,
186+ Arc :: clone ( & terminating) ,
187+ )
188+ . unwrap ( ) ;
189+
171190 let args: Args = Docopt :: new ( USAGE )
172191 . and_then ( |d| d. deserialize ( ) )
173192 . unwrap_or_else ( |e| e. exit ( ) ) ;
174193
175194 debug ! ( "ARGS {:?}" , args) ;
176195
177- let default_config = String :: from ( "workload.toml" ) ;
178196 let duration_timer = SystemTime :: now ( ) ;
179197 let script_path = args. flag_f ;
180- let config_path = args. flag_c . unwrap_or ( default_config) ;
181-
182- let config = Config :: builder ( )
183- // Add in `./Settings.toml`
184- . add_source (
185- config:: File :: with_name ( "/etc/berserker/workload.toml" )
186- . required ( false ) ,
187- )
188- . add_source (
189- config:: File :: with_name ( config_path. as_str ( ) ) . required ( false ) ,
190- )
191- // Add in settings from the environment (with a prefix of APP)
192- // Eg.. `BERSERKER__WORKLOAD__ARRIVAL_RATE=1` would set the `arrival_rate` key
193- . add_source (
194- config:: Environment :: with_prefix ( "BERSERKER" )
195- . try_parsing ( true )
196- . separator ( "__" ) ,
197- )
198- . build ( )
199- . unwrap ( )
200- . try_deserialize :: < WorkloadConfig > ( )
201- . unwrap ( ) ;
202-
203- info ! ( "Config: {:?}" , config) ;
204198
205199 let handles = match script_path {
206200 Some ( path) => run_script ( path) ,
207- None => run_workload ( config) ,
201+ None => {
202+ let default_config = String :: from ( "workload.toml" ) ;
203+ let config_path = args. flag_c . unwrap_or ( default_config) ;
204+
205+ let config = Config :: builder ( )
206+ // Add in `./Settings.toml`
207+ . add_source (
208+ config:: File :: with_name ( "/etc/berserker/workload.toml" )
209+ . required ( false ) ,
210+ )
211+ . add_source (
212+ config:: File :: with_name ( config_path. as_str ( ) )
213+ . required ( false ) ,
214+ )
215+ // Add in settings from the environment (with a prefix of APP)
216+ // Eg.. `BERSERKER__WORKLOAD__ARRIVAL_RATE=1` would set the `arrival_rate` key
217+ . add_source (
218+ config:: Environment :: with_prefix ( "BERSERKER" )
219+ . try_parsing ( true )
220+ . separator ( "__" ) ,
221+ )
222+ . build ( )
223+ . unwrap ( )
224+ . try_deserialize :: < WorkloadConfig > ( )
225+ . unwrap ( ) ;
226+
227+ info ! ( "Config: {:?}" , config) ;
228+ run_workload ( config)
229+ }
208230 } ;
209231
210232 let processes = & handles. clone ( ) ;
211233
212234 thread:: scope ( |s| {
213- if config. duration != 0 {
214- // Spin a watcher thread
215- s. spawn ( move || loop {
216- thread:: sleep ( time:: Duration :: from_secs ( 1 ) ) ;
217- let elapsed = duration_timer. elapsed ( ) . unwrap ( ) . as_secs ( ) ;
218-
219- if elapsed > config. duration {
220- for handle in processes. iter ( ) . flatten ( ) {
221- info ! ( "Terminating: {}" , * handle) ;
222- let _ = kill ( Pid :: from_raw ( * handle) , Signal :: SIGTERM ) ;
223- }
235+ // Spin a watcher thread
236+ s. spawn ( move || loop {
237+ thread:: sleep ( time:: Duration :: from_secs ( 1 ) ) ;
238+ let elapsed = duration_timer. elapsed ( ) . unwrap ( ) . as_secs ( ) ;
239+
240+ // Find all processes with expired duration. If we've received
241+ // SIGTERM, get all processes.
242+ let expired = processes
243+ . iter ( )
244+ . filter ( |( _, duration) | {
245+ ( * duration > 0 && * duration < elapsed)
246+ || terminating. load ( Ordering :: Relaxed )
247+ } )
248+ . collect :: < Vec < _ > > ( ) ;
249+
250+ for ( handle, _) in & expired {
251+ info ! ( "Terminating: {}" , * handle) ;
252+ let _ = kill ( Pid :: from_raw ( * handle) , Signal :: SIGKILL ) ;
253+ }
224254
225- break ;
226- }
227- } ) ;
228- }
255+ if expired . len ( ) == processes . len ( ) {
256+ break ;
257+ }
258+ } ) ;
229259
230260 s. spawn ( move || {
231- for handle in processes . iter ( ) . flatten ( ) {
232- info ! ( "waitpid: {}" , * handle) ;
233- match waitpid ( Pid :: from_raw ( * handle) , None ) {
261+ for ( handle, _ ) in handles {
262+ info ! ( "waitpid: {}" , handle) ;
263+ match waitpid ( Pid :: from_raw ( handle) , None ) {
234264 Ok ( _) => {
235- info ! ( "{:?} stopped" , * handle )
265+ info ! ( "{handle :?} stopped" )
236266 }
237267 Err ( Errno :: ECHILD ) => {
238- info ! { "no process {:?} found" , * handle }
268+ info ! ( "no process {handle :?} found" )
239269 }
240270 Err ( e) => {
241- panic ! { "cannot wait for {:?}: {:?} " , * handle , e }
271+ panic ! ( "cannot wait for {handle :?}: {e :?}" )
242272 }
243273 } ;
244274 }
0 commit comments