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

nasa / trick / 25521106662

07 May 2026 08:45PM UTC coverage: 56.776%. First build
25521106662

Pull #2099

github

web-flow
Merge 5bba2b086 into 9b86789c4
Pull Request #2099: Fixes to MSSharedMem class

0 of 9 new or added lines in 2 files covered. (0.0%)

12916 of 22749 relevant lines covered (56.78%)

299013.8 hits per line

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

11.2
/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 */
NEW
180
    if(connection->accept())
×
181
    {
NEW
182
        perror("Slave_accept");
×
NEW
183
        return (-3) ;  
×
184
    }
185

186
    /* @li Set the synchronization wait limit */
187
    connection->set_sync_wait_limit(sync_wait_limit) ;
×
188

189
    /* Set slave to activated */
190
    activated = true;
×
191

192
    return (0) ;
×
193
}
194

195
int Trick::SlaveInfo::read_slave_status() {
×
196

197
    MS_SIM_COMMAND slave_command ;
198
    MS_SIM_COMMAND exec_command ;
199

200
    /** @par Detailed Design: */
201
    /** @li If the slave is an active synchronization partner (activated == true) */
202
    if (activated == true) {
×
203

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

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

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

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

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

305
    return(0) ;
×
306
}
307

308
Trick::Master::Master() {
183✔
309
    enabled = false ;
183✔
310
    the_ms_master = this ;
183✔
311
    num_slaves = 0 ;
183✔
312
}
183✔
313

314
int Trick::Master::enable() {
×
315
    enabled = true ;
×
316
    return(0);
×
317
}
318

319
int Trick::Master::disable() {
×
320
    enabled = false ;
×
321
    return(0);
×
322
}
323

324
Trick::SlaveInfo * Trick::Master::add_slave(Trick::SlaveInfo * new_slave) {
×
325

326

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

333
    /** @li push the slave onto the vector of slaves */
334
    slaves.push_back(new_slave) ;
×
335

336
    num_slaves++ ;
×
337

338
    /** @li return the address of the new slave */
339
    return(new_slave) ;
×
340

341
}
342

343
int Trick::Master::process_sim_args() {
180✔
344

345
    return(0) ;
180✔
346
}
347

348
int Trick::Master::init() {
180✔
349

350
    unsigned int ii ;
351

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

374
        // Freezes are only allowed on frame boundaries when Master/Slave is enabled.
375
        exec_set_freeze_on_frame_boundary(true) ;
×
376

377
    }
378

379
    return(0) ;
180✔
380
}
381

382
/**
383
@details
384
-# Read the status of all slaves
385
*/
386
int Trick::Master::end_of_frame_status_from_slave() {
3,685✔
387
    unsigned int ii ;
388
    if ( enabled ) {
3,685✔
389
        for ( ii = 0 ; ii < slaves.size() ; ii++ ) {
×
390
            slaves[ii]->read_slave_status() ;
×
391
        }
392
    }
393

394
    return(0) ;
3,685✔
395
}
396

397
/**
398
@details
399
-# Write the master status to all slaves
400
*/
401
int Trick::Master::end_of_frame_status_to_slave() {
3,685✔
402
    unsigned int ii ;
403
    if ( enabled ) {
3,685✔
404
        for ( ii = 0 ; ii < slaves.size() ; ii++ ) {
×
405
            slaves[ii]->write_master_status() ;
×
406
        }
407
    }
408

409
    return(0) ;
3,685✔
410
}
411

412
int Trick::Master::freeze_init() {
×
413

414
    unsigned int ii ;
415
    Trick::SlaveInfo * curr_slave ;
416

417
    if ( enabled ) {
×
418
        /** @par Detailed Design: */
419
        /** @li Set the connection sync_wait_limit to infinite as we enter freeze */
420
        for ( ii = 0 ; ii < slaves.size() ; ii++ ) {
×
421
            curr_slave = slaves[ii] ;
×
422
            if ( curr_slave->activated == true ) {
×
423
                curr_slave->connection->set_sync_wait_limit(-1.0) ;
×
424
            }
425
        }
426
    }
427
    return(0) ;
×
428

429
}
430

431
int Trick::Master::freeze() {
×
432
    /** @par Detailed Design: */
433
    /** @li Sync with slave(s) */
434
    end_of_frame_status_from_slave() ;
×
435
    end_of_frame_status_to_slave() ;
×
436
    return(0) ;
×
437
}
438

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

493
int Trick::Master::unfreeze() {
×
494

495
    unsigned int ii ;
496
    Trick::SlaveInfo * curr_slave ;
497

498
    if ( enabled ) {
×
499
        for ( ii = 0 ; ii < slaves.size() ; ii++ ) {
×
500
            /** @par Detailed Design: */
501
            /** @li Set the connection sync_wait_limit to the default for each slave */
502
            curr_slave = slaves[ii] ;
×
503
            if ( curr_slave->activated == true ) {
×
504
                curr_slave->connection->set_sync_wait_limit(curr_slave->sync_wait_limit) ;
×
505
            }
506
        }
507
    }
508
    return(0) ;
×
509

510
}
511

512
int Trick::Master::shutdown() {
150✔
513
    /** @par Detailed Design: */
514
    /** @li Sync with slave(s) */
515
    if ( enabled ) {
150✔
516
        end_of_frame_status_from_slave() ;
×
517
        end_of_frame_status_to_slave() ;
×
518
    }
519
    return(0) ;
150✔
520
}
521

522
/**
523
 * @relates Trick::Master
524
 * C binded function to toggle the master/slave synchronization flag to on.
525
 * @return always 0
526
 */
527
extern "C" int ms_master_enable(void) {
×
528
    the_ms_master->enable() ;
×
529
    return(0) ;
×
530
}
531

532
/**
533
 * @relates Trick::Master
534
 * C binded function to toggle the master/slave synchronization flag to off.
535
 * @return always 0
536
 */
537
extern "C" int ms_master_disable(void) {
×
538
    the_ms_master->disable() ;
×
539
    return(0) ;
×
540
}
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