• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

nasa / trick / 25456501308

06 May 2026 07:29PM UTC coverage: 55.935% (-0.8%) from 56.7%
25456501308

Pull #2011

github

web-flow
Merge 7ad262960 into 7054e405e
Pull Request #2011: Single-file CI and code style adoption

14612 of 26123 relevant lines covered (55.94%)

462107.16 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

11.3
/trick_source/sim_services/MasterSlave/Master.cpp
1
/*
2
   PURPOSE: (Master for master/slave syncrhonization)
3
 */
4

5
#include <iostream>
6
#include <sstream>
7
#include <string>
8
#include <pwd.h>
9
#include <stdlib.h>
10
#include <unistd.h>
11

12
#include <sys/types.h>
13
#include <sys/wait.h>
14

15
#include "trick/Master.hh"
16
#include "trick/master_proto.h"
17
#include "trick/MSSocket.hh"
18
#include "trick/MSSharedMem.hh"
19
#include "trick/exec_proto.h"
20
#include "trick/sim_mode.h"
21
#include "trick/command_line_protos.h"
22
#include "trick/unix_commands.h"
23
#include "trick/message_proto.h"
24
#include "trick/message_type.h"
25
#include "trick/env_proto.h"
26
#include "trick/CheckPointRestart_c_intf.hh" // for checkpoint_get_output_file
27

28
Trick::Master * the_ms_master ;
29

30
Trick::SlaveInfo::SlaveInfo() {
×
31

32
    enabled = true ;
×
33
    activated = false ;
×
34
    slave_type = "undefined";
×
35

36
    /* default the slaves to start with ssh */
37
    remote_shell = TRICK_SSH ;
×
38

39
    /* default the master not to quit if a slave dies */
40
    sync_error_terminate = false ;
×
41

42
    /* default the slaves to start on the localhost */
43
    machine_name = "localhost" ;
×
44

45
    sync_wait_limit = 0.0 ;
×
46
    reconnect_wait_limit = 0.0 ;
×
47
    reconnect_count = 0;
×
48
    chkpnt_dump_auto = true ;
×
49
    chkpnt_load_auto = true ;
×
50
}
×
51

52
int Trick::SlaveInfo::set_connection_type(Trick::MSConnect * in_connection) {
×
53

54
    connection = in_connection ;
×
55
    return(0) ;
×
56
}
57

58
int Trick::SlaveInfo::start() {
×
59

60
    int arg_i;
61
    char *display;
62

63
    std::stringstream startup_command ;
×
64
    std::stringstream temp_stream ;
×
65

66
    struct passwd *passp;
67

68
    int argc ;
69
    char ** argv ;
70

71
    std::string slave = "undefined";
×
72

73
    /** @par Detailed Design */
74
    argc = command_line_args_get_argc() ;
×
75
    argv = command_line_args_get_argv() ;
×
76

77
    /** @li If the slave connection type is not specified return an error */
78
    if ( connection == NULL ) {
×
79
        message_publish(MSG_ERROR , "Slave connection type not specified.\n") ;
×
80
        return(-1) ;
×
81
    }
82

83
    /** @li Start remote startup command with remote shell to use and remote machine name*/
84
    switch ( remote_shell ) {
×
85
        case TRICK_RSH:
×
86
            startup_command << unix_rsh << " " << remote_shell_args ;
×
87
            break;
×
88
        case TRICK_USER_REMOTE_SH:
×
89
            if ( user_remote_shell.empty() ) {
×
90
                message_publish(MSG_WARNING , "TRICK_USER_REMOTE_SH specified for Slave startup, but no shell given.\nDefaulting to %s.\n", unix_ssh) ;
×
91
                user_remote_shell = unix_ssh ;
×
92
            }
93
            startup_command << user_remote_shell << " " << remote_shell_args ;
×
94
            break;
×
95
        case TRICK_SSH:
×
96
        default:
97
            startup_command << unix_ssh << " " << remote_shell_args ;
×
98
            break;
×
99
    }
100
    startup_command << " " << machine_name ;
×
101

102
    /** @li Add the remote display. If a remote display has not been specified, use the environment's */
103
    if ( machine_display.empty() ) {
×
104
        if ((display = (char *) env_get_var("DISPLAY")) == NULL) {
×
105
            message_publish(MSG_ERROR, "Cannot get environment variable $DISPLAY for Slave.\n") ;
×
106
        } else {
107
            machine_display = display ;
×
108
        }
109
    }
110

111
    /** @li cd to the simulation path in the remote startup command */
112
    if ( sim_path.empty() ) {
×
113
        message_publish(MSG_ERROR, "Slave startup sim_path is empty.\n") ;
×
114
        return(-1) ;
×
115
    }
116
    /** @li Set the DISPLAY environment variable in the remote startup command.
117
            Use setenv for *csh or export for all other shells */
118
    passp = getpwuid(getuid());
×
119
    if ( ! machine_display.empty() ) {
×
120
        if (strstr(passp->pw_shell, "csh")) {
×
121
            startup_command << " 'setenv DISPLAY " << machine_display ;
×
122
        } else {
123
            startup_command << " 'export DISPLAY=" << machine_display ;
×
124
        }
125
        startup_command << " ; cd " << sim_path << " ; " ;
×
126
    } else {
127
        startup_command << " 'cd " << sim_path << " ; " ;
×
128
    }
129

130
    /** @li Set up remote shell environment if needed for the remote startup command */
131
    if ( !remote_shell_config_file.empty() ) {
×
132
        startup_command << " source " << remote_shell_config_file << " ; " ;
×
133
        // cd to sim_path directory again if in case the config file changed pwd 
134
        // to a different directory other than the sim_path
135
        startup_command << " cd " << sim_path << " ; " ;
×
136
    }
137

138
    if (strstr(passp->pw_shell, "csh")) {
×
139
        startup_command << " setenv TRICK_HOST_CPU `trick-gte TRICK_HOST_CPU` ; " ;
×
140
    } else {
141
        startup_command << " export TRICK_HOST_CPU=`trick-gte TRICK_HOST_CPU` ; " ;
×
142
    }
143

144

145
    /** @li start the simulation in the remote startup command */
146
    if ( S_main_name.empty() ) {
×
147
        S_main_name = "./S_main_${TRICK_HOST_CPU}.exe" ;
×
148
    }
149
    startup_command << S_main_name ;
×
150

151
    /** @li add the user provided run input file if provided */
152
    if ( ! run_input_file.empty() ) {
×
153

154
        startup_command << " " << run_input_file ;
×
155
    }
156

157
    /** @li Add the connection specific arguments to the startup command */
158
    startup_command << " " << connection->add_sim_args( slave_type ) ;
×
159

160
    /* @li Add the additional user arguments to the remote command */
161
    if ( ! other_args.empty() ) {
×
162
        startup_command << " -u " << other_args ;
×
163
    }
164
    for (arg_i = 2; arg_i < argc ; arg_i++) {
×
165
        startup_command << " " << argv[arg_i] ;
×
166
    }
167

168
    /* @li start the remote command in the background */
169
    startup_command << "' &"  ;
×
170

171
    message_publish(MSG_INFO, startup_command.str().c_str()) ;
×
172

173
    /* @li Execute the startup command */
174
    if (system(startup_command.str().c_str())) {
×
175
        perror("Slave_start");
×
176
        return (-2) ;
×
177
    }
178

179
    /* @li Wait for the slave to connect to the master */
180
    connection->accept() ;
×
181

182
    /* @li Set the synchronization wait limit */
183
    connection->set_sync_wait_limit(sync_wait_limit) ;
×
184

185
    /* Set slave to activated */
186
    activated = true;
×
187

188
    return (0) ;
×
189
}
×
190

191
int Trick::SlaveInfo::read_slave_status() {
×
192

193
    MS_SIM_COMMAND slave_command ;
194
    MS_SIM_COMMAND exec_command ;
195

196
    /** @par Detailed Design: */
197
    /** @li If the slave is an active synchronization partner (activated == true) */
198
    if (activated == true) {
×
199

200
        /** @li read the current slave exec_command */
201
        slave_command = connection->read_command() ;
×
202
        //printf("DEBUG master read %d command from slave\n", slave_command);fflush(stdout);
203

204
        exec_command = (MS_SIM_COMMAND)exec_get_exec_command() ;
×
205
        // fixup: is it possible we won't get slave's Exit command over socket when it terminates?, set it here if that happens
206
        if (dynamic_cast<MSSocket*>(connection)) {
×
207
            if ((slave_command == MS_ErrorCmd) && (reconnect_wait_limit > 0.0) && (reconnect_count == 0)) {
×
208
                slave_command = MS_ExitCmd;
×
209
            }
210
        }
211

212
        /** @li If the master is not currently exiting, change modes if the slave is freezing/exiting or has an error */
213
        if ( exec_command != MS_ExitCmd ) {
×
214
            switch ( slave_command ) {
×
215
                case (MS_ErrorCmd):
×
216
                    /** @li if the user has set a reconnect_wait_limit, continue on if we are still under that limit, otherwise
217
                            if the current slave mode cannot be read, exit the master if sync_error_terminate == true,
218
                            otherwise set the activated flag to false */
219
                    if ( (reconnect_count * exec_get_freeze_frame()) < reconnect_wait_limit) {
×
220
                        reconnect_count++;
×
221
                    } else if (sync_error_terminate == true) {
×
222
                        message_publish(MSG_ERROR, "Master lost sync with slave, so master is terminating.\n") ;
×
223
                        exec_terminate_with_return(-1, __FILE__, __LINE__ , "Master lost sync with slave.") ;
×
224
                    }
225
                    else {
226
                        message_publish(MSG_ERROR, "Master lost sync with slave, so slave is being deactivated.\n") ;
×
227
                        activated = false ;
×
228
                        return(0) ;
×
229
                    }
230
                    break ;
×
231
                case (MS_ExitCmd):
×
232
                    /** @li if the current slave mode is exiting, exit the master if sync_error_terminate == true.
233
                            otherwise wait for slave to reconnect. when wait limit is 0, set the activated flag to false */
234
                    if  (sync_error_terminate == true){
×
235
                        message_publish(MSG_WARNING, "sync_error_terminate is true: Slave is exiting, so master is terminating.\n") ;
×
236
                        exec_terminate_with_return(-1, __FILE__, __LINE__ , "Slave is exiting, so is the master.") ;
×
237
                    }
238
                    else {
239
                        message_publish(MSG_WARNING, "Slave is exiting.\n") ;
×
240
                        // if reconnect_wait_limit is set, master waits for slave to reconnect
241
                        if (reconnect_wait_limit > 0.0) {
×
242
                            message_publish(MSG_WARNING, "Master will wait %f seconds for slave to reconnect.\n", reconnect_wait_limit) ;
×
243
                            // make reads (shared mem connection) return quickly so we don't overrun waiting for reconnect
244
                            connection->set_sync_wait_limit(exec_get_freeze_frame());
×
245
                        }
246
                        else {
247
                            message_publish(MSG_WARNING, "reconnect_wait_limit: 0.0 - Master will stop communicating with slave.\n") ;
×
248
                            activated = false ;
×
249
                        }
250
                        return(0) ;
×
251
                    }
252
                    break ;
×
253
                case (MS_FreezeCmd):
×
254
                    /** @li if the current slave is freezing, freeze the master too */
255
                    message_publish(MSG_INFO, "Slave is freezing.\n") ;
×
256
                    exec_set_exec_command(FreezeCmd) ;
×
257
                    reconnect_count = 0;
×
258
                    break ;
×
259
                case (MS_ReconnectCmd):
×
260
                    // set the sync wait limit back to its default
261
                    connection->set_sync_wait_limit(sync_wait_limit);
×
262
                    message_publish(MSG_INFO, "Master has reconnected to slave.\n") ;
×
263
                    reconnect_count = 0;
×
264
                    break ;
×
265
                default:
×
266
                    break ;
×
267
            }
268
        }
269
    }
270
    return(0) ;
×
271
}
272

273
int Trick::SlaveInfo::write_master_status() {
×
274
    /** @par Detailed Design: */
275
    /** @li If the slave is an active synchronization partner (activated == true) */
276
    /** @li and we are not currently waiting for slave to reconnect, */
277
    if (( activated == true ) && (reconnect_count == 0)) {
×
278
        /** @li write the current time according to the master to the slave */
279
        connection->write_time(exec_get_time_tics()) ;
×
280
        /** @li write the current exec_command according to the master to the slave */
281
        connection->write_command((MS_SIM_COMMAND)exec_get_exec_command()) ;
×
282
    }
283
    return(0) ;
×
284
}
285

286
int Trick::SlaveInfo::write_master_chkpnt_name(std::string full_path_name) {
×
287
    /** @par Detailed Design: */
288
    /** @li If the slave is an active synchronization partner (activated == true) */
289
    if ( activated == true ) {
×
290
        /** @li write the checkpoint dir/filename to the slave */
291
        if (full_path_name.length() > sizeof(chkpnt_name)-1) {
×
292
            message_publish(MSG_ERROR, "Master could not send checkpoint name to slave because name too long (max = %d).\n",
×
293
                            sizeof(chkpnt_name)) ;
294
            chkpnt_name[0] = MS_ERROR_NAME; // send error character
×
295
        } else {
296
            strcpy(chkpnt_name, full_path_name.c_str());
×
297
        }
298
        connection->write_name(chkpnt_name, sizeof(chkpnt_name)) ;
×
299
    }
300

301
    return(0) ;
×
302
}
303

304
Trick::Master::Master() {
183✔
305
    enabled = false ;
183✔
306
    the_ms_master = this ;
183✔
307
    num_slaves = 0 ;
183✔
308
}
183✔
309

310
int Trick::Master::enable() {
×
311
    enabled = true ;
×
312
    return(0);
×
313
}
314

315
int Trick::Master::disable() {
×
316
    enabled = false ;
×
317
    return(0);
×
318
}
319

320
Trick::SlaveInfo * Trick::Master::add_slave(Trick::SlaveInfo * new_slave) {
×
321

322

323
    /** @par Detailed Design: */
324
    /** @li if the incoming parameter is NULL or not specified allocate space for a new slave */
325
    if (new_slave == NULL ) {
×
326
        new_slave = new Trick::SlaveInfo ;
×
327
    }
328

329
    /** @li push the slave onto the vector of slaves */
330
    slaves.push_back(new_slave) ;
×
331

332
    num_slaves++ ;
×
333

334
    /** @li return the address of the new slave */
335
    return(new_slave) ;
×
336

337
}
338

339
int Trick::Master::process_sim_args() {
180✔
340

341
    return(0) ;
180✔
342
}
343

344
int Trick::Master::init() {
180✔
345

346
    unsigned int ii ;
347

348
    /** @li Call Trick::SlaveInfo::start() for each slave */
349
    if ( enabled ) {
180✔
350
        for ( ii = 0 ; ii < slaves.size() ; ii++ ) {
×
351
            slaves[ii]->start() ;
×
352
            /** @li Write the master's software frame to each slave */
353
            slaves[ii]->connection->write_time(exec_get_software_frame_tics()) ;
×
354
            /** @li Write the master's time tic value to each slave */
355
            slaves[ii]->connection->write_time(exec_get_time_tic_value()) ;
×
356
            /** @li Write the master's sync_wait_limit to each slave */
357
            slaves[ii]->connection->write_time((long long)(slaves[ii]->sync_wait_limit * exec_get_time_tic_value())) ;
×
358
            /** @li Cast the freeze command and write it to the slave */
359
            slaves[ii]->connection->write_command((MS_SIM_COMMAND)exec_get_freeze_command()) ;
×
360
            /** @li Write a flag word containing the checkpoint pre_init, post_init, and end flags to the slave */
361
            slaves[ii]->connection->write_time((long long) ((get_checkpoint_pre_init() << 2) +
×
362
                                                            (get_checkpoint_post_init() << 1) +
×
363
                                                            (get_checkpoint_end())) );
×
364
        }
365

366
        // Freezes are only allowed on frame boundaries when Master/Slave is enabled.
367
        exec_set_freeze_on_frame_boundary(true) ;
×
368

369
    }
370

371
    return(0) ;
180✔
372
}
373

374
/**
375
@details
376
-# Read the status of all slaves
377
*/
378
int Trick::Master::end_of_frame_status_from_slave() {
3,685✔
379
    unsigned int ii ;
380
    if ( enabled ) {
3,685✔
381
        for ( ii = 0 ; ii < slaves.size() ; ii++ ) {
×
382
            slaves[ii]->read_slave_status() ;
×
383
        }
384
    }
385

386
    return(0) ;
3,685✔
387
}
388

389
/**
390
@details
391
-# Write the master status to all slaves
392
*/
393
int Trick::Master::end_of_frame_status_to_slave() {
3,685✔
394
    unsigned int ii ;
395
    if ( enabled ) {
3,685✔
396
        for ( ii = 0 ; ii < slaves.size() ; ii++ ) {
×
397
            slaves[ii]->write_master_status() ;
×
398
        }
399
    }
400

401
    return(0) ;
3,685✔
402
}
403

404
int Trick::Master::freeze_init() {
×
405

406
    unsigned int ii ;
407
    Trick::SlaveInfo * curr_slave ;
408

409
    if ( enabled ) {
×
410
        /** @par Detailed Design: */
411
        /** @li Set the connection sync_wait_limit to infinite as we enter freeze */
412
        for ( ii = 0 ; ii < slaves.size() ; ii++ ) {
×
413
            curr_slave = slaves[ii] ;
×
414
            if ( curr_slave->activated == true ) {
×
415
                curr_slave->connection->set_sync_wait_limit(-1.0) ;
×
416
            }
417
        }
418
    }
419
    return(0) ;
×
420

421
}
422

423
int Trick::Master::freeze() {
×
424
    /** @par Detailed Design: */
425
    /** @li Sync with slave(s) */
426
    end_of_frame_status_from_slave() ;
×
427
    end_of_frame_status_to_slave() ;
×
428
    return(0) ;
×
429
}
430

431
int Trick::Master::checkpoint() {
17✔
432
    /** @par Detailed Design: */
433
    /** @li If chkpnt_dump_auto, tell slave to dump a checkpoint */
434
    unsigned int ii ;
435
    // do not tell slave to dump if this is a pre_init, post_init, or end checkpoint
436
    // those are handled with flags sent to slave in init()
437
    if ((exec_get_mode() == Initialization) || (exec_get_mode() == ExitMode)) {
17✔
438
        return(0);
1✔
439
    }
440
    if (enabled) {
16✔
441
        // Use 2 loops to read all slave status before writing any status out.
442
        for ( ii = 0 ; ii < slaves.size() ; ii++ ) {
×
443
            slaves[ii]->read_slave_status() ;
×
444
        }
445
        SIM_COMMAND save_command = exec_get_exec_command() ;
×
446
        std::string full_path_name = checkpoint_get_output_file();
×
447
        for ( ii = 0 ; ii < slaves.size() ; ii++ ) {
×
448
            if (slaves[ii]->chkpnt_dump_auto) {
×
449
                exec_set_exec_command((SIM_COMMAND)MS_ChkpntDumpAsciiCmd) ;
×
450
                slaves[ii]->write_master_status() ;
×
451
                slaves[ii]->write_master_chkpnt_name(full_path_name) ;
×
452
                exec_set_exec_command(save_command) ;
×
453
            } else { // no auto dump
454
                slaves[ii]->write_master_status() ;
×
455
            }
456
        }
457
    }
×
458
    return(0) ;
16✔
459
}
460
int Trick::Master::preload_checkpoint() {
11✔
461
    /** @par Detailed Design: */
462
    /** @li If chkpnt_load_auto, tell slave to load a checkpoint */
463
    unsigned int ii ;
464
    if (enabled) {
11✔
465
        // Use 2 loops to read all slave status before writing any status out.
466
        for ( ii = 0 ; ii < slaves.size() ; ii++ ) {
×
467
            slaves[ii]->read_slave_status() ;
×
468
        }
469
        SIM_COMMAND save_command = exec_get_exec_command() ;
×
470
        std::string full_path_name = checkpoint_get_load_file();
×
471
        for ( ii = 0 ; ii < slaves.size() ; ii++ ) {
×
472
            if (slaves[ii]->chkpnt_load_auto) {
×
473
                exec_set_exec_command((SIM_COMMAND)MS_ChkpntLoadAsciiCmd) ;
×
474
                slaves[ii]->write_master_status() ;
×
475
                slaves[ii]->write_master_chkpnt_name(full_path_name) ;
×
476
                exec_set_exec_command(save_command) ;
×
477
            } else { // no auto load
478
                slaves[ii]->write_master_status() ;
×
479
            }
480
        }
481
    }
×
482
    return(0) ;
11✔
483
}
484

485
int Trick::Master::unfreeze() {
×
486

487
    unsigned int ii ;
488
    Trick::SlaveInfo * curr_slave ;
489

490
    if ( enabled ) {
×
491
        for ( ii = 0 ; ii < slaves.size() ; ii++ ) {
×
492
            /** @par Detailed Design: */
493
            /** @li Set the connection sync_wait_limit to the default for each slave */
494
            curr_slave = slaves[ii] ;
×
495
            if ( curr_slave->activated == true ) {
×
496
                curr_slave->connection->set_sync_wait_limit(curr_slave->sync_wait_limit) ;
×
497
            }
498
        }
499
    }
500
    return(0) ;
×
501

502
}
503

504
int Trick::Master::shutdown() {
150✔
505
    /** @par Detailed Design: */
506
    /** @li Sync with slave(s) */
507
    if ( enabled ) {
150✔
508
        end_of_frame_status_from_slave() ;
×
509
        end_of_frame_status_to_slave() ;
×
510
    }
511
    return(0) ;
150✔
512
}
513

514
/**
515
 * @relates Trick::Master
516
 * C binded function to toggle the master/slave synchronization flag to on.
517
 * @return always 0
518
 */
519
extern "C" int ms_master_enable(void) {
×
520
    the_ms_master->enable() ;
×
521
    return(0) ;
×
522
}
523

524
/**
525
 * @relates Trick::Master
526
 * C binded function to toggle the master/slave synchronization flag to off.
527
 * @return always 0
528
 */
529
extern "C" int ms_master_disable(void) {
×
530
    the_ms_master->disable() ;
×
531
    return(0) ;
×
532
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc