Skip to content

Commit ebb91ba

Browse files
committed
addressing crash when G4VisExecutive is destroyed. fixing show_gemc_installation and dockerfile_creator.py to use new path
1 parent 5dd1e98 commit ebb91ba

7 files changed

Lines changed: 49 additions & 25 deletions

File tree

ci/build.sh

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
# Purpose: compiles gemc with optional sanitizers or debugging options.
44

55
# Container run:
6-
# docker run --rm -it ghcr.io/gemc/g4install:11.4.1-fedora-40 bash -li
6+
# docker run --rm -it ghcr.io/gemc/g4install:11.4.1-ubuntu-24.04 bash -li
77
# git clone http://github.com/gemc/src /root/src && cd /root/src
88
# ./ci/build.sh
99

@@ -108,6 +108,6 @@ else
108108
echo " > Meson Tests Successful"
109109
echo
110110
fi
111-
echo " - Successful: $(grep -m1 'Ok:' "$test_log" | awk '{print $2}')" | tee -a "$test_log"
112-
echo " - Failures: $(grep -m1 'Fail:' "$test_log" | awk '{print $2}')" | tee -a "$test_log"
111+
echo " - Successful: $(grep 'Ok:' "$test_log" | tail -n 1 | awk '{print $2}')" | tee -a "$test_log"
112+
echo " - Failures: $(grep 'Fail:' "$test_log" | tail -n 1 | awk '{print $2}')" | tee -a "$test_log"
113113
echo " > Complete test log: $test_log"

ci/dockerfile_creator.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ def install_gemc(geant4_version: str, gemc_version: str) -> str:
4646
commands += f" && DOCKER_ENTRYPOINT_SOURCE_ONLY=1 . {remote_entrypoint()} \\\n"
4747
commands += f' && module load geant4/{geant4_version} \\\n'
4848
commands += f' && ./ci/build.sh \\\n'
49-
commands += f' && echo ". \\${{SIM_HOME}}/gemc/gemc.sh" >> {remote_entrypoint_addon()} \n'
49+
commands += f' && echo ". \\${{SIM_HOME}}/gemc/dev/gemc.sh" >> {remote_entrypoint_addon()} \n'
5050
return commands
5151

5252

ci/env.sh

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,26 +11,27 @@ function enable_git_describe {
1111
}
1212

1313
function show_gemc_installation {
14-
1514
{
16-
echo "- Content of \$GEMC=$GEMC"
17-
ls -lrt $GEMC
15+
local install_dir="${SIM_HOME:?SIM_HOME not set}/gemc/dev"
16+
17+
echo "- Content of $install_dir=$install_dir"
18+
ls -lrt $install_dir
1819

19-
echo "- Content of \$GEMC/bin=$GEMC/bin"
20-
ls -lrt $GEMC/bin
20+
echo "- Content of \$install_dir/bin=$install_dir/bin"
21+
ls -lrt $install_dir/bin
2122

22-
if [ -d $GEMC/lib ]; then
23-
echo "- Content of \$GEMC/lib=$GEMC/lib"
24-
ls -lrt $GEMC/lib
23+
if [ -d $install_dir/lib ]; then
24+
echo "- Content of \$install_dir/lib=$install_dir/lib"
25+
ls -lrt $install_dir/lib
2526
fi
2627

27-
echo " ldd of $GEMC/bin/gemc:"
28+
echo " ldd of $install_dir/bin/gemc:"
2829

2930
# if on unix, use ldd , if on mac, use otool -L
3031
if [[ "$(uname)" == "Darwin" ]]; then
31-
otool -L $GEMC/bin/gemc
32+
otool -L $install_dir/bin/gemc
3233
else
33-
ldd $GEMC/bin/gemc
34+
ldd $install_dir/bin/gemc
3435
fi
3536

3637
echo " > To check gemc installation: cat $gemc_install_show"

gemc.cc

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@
1717
#include "gaction.h"
1818
#include "gstreamer.h"
1919

20+
// c++
21+
#include <cstdio>
22+
#include <cstdlib>
23+
2024
int main(int argc, char* argv[]) {
2125

2226
auto gopts = std::make_shared<GOptions>(argc, argv, gemc::defineOptions());
@@ -37,14 +41,13 @@ int main(int argc, char* argv[]) {
3741
// random engine set by options
3842
gemc::start_random_engine(gopts, log);
3943

40-
// Pre-load streamer plugins before Geant4 creates worker threads. Sanitized Linux
41-
// builds can fail late dlopen() calls from workers with static TLS exhaustion.
42-
auto preloaded_gstreamer_map = gstreamer::preloadGStreamerPlugins(gopts);
43-
(void) preloaded_gstreamer_map;
44-
4544
// init geant4 run manager with then number of threads coming from options. always fails if unavailable
4645
auto runManager = std::unique_ptr<G4RunManager>(G4RunManagerFactory::CreateRunManager(G4RunManagerType::Default, true, nthreads));
4746

47+
// Pre-load streamer plugins before Geant4 creates worker threads. Sanitized Linux
48+
// builds can fail late dlopen() calls from workers with static TLS exhaustion.
49+
// Declaring this after runManager makes it destruct before runManager at shutdown.
50+
auto preloaded_gstreamer_map = gstreamer::preloadGStreamerPlugins(gopts);
4851

4952
// must be a raw pointer because geant4 takes ownership
5053
auto gdetector = new GDetectorConstruction(gopts);
@@ -111,6 +114,7 @@ int main(int argc, char* argv[]) {
111114
spash_screen->finish(&gemcGui);
112115
app_result = QApplication::exec();
113116

117+
preloaded_gstreamer_map.reset();
114118
runManager.reset();
115119
delete g4SceneProperties;
116120
delete uiQtSession;
@@ -133,10 +137,20 @@ int main(int argc, char* argv[]) {
133137
delete session;
134138
}
135139

136-
137-
delete visManager;
140+
geventDispenser.reset();
141+
preloaded_gstreamer_map.reset();
142+
runManager.reset();
143+
// Avoid explicit G4VisExecutive teardown in batch shutdown. On Linux/Geant4 11.4
144+
// this can double-free the process-wide G4Colour table during static exit.
138145

139146
log->info(0, "Simulation completed, arrivederci! ");
140147

148+
#if defined(__linux__)
149+
if (!gui) {
150+
std::fflush(nullptr);
151+
std::_Exit(app_result);
152+
}
153+
#endif
154+
141155
return app_result;
142156
}

gfactory/gdl.h

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -152,16 +152,22 @@ dlhandle load_lib(const std::string& lib) // never throws
152152
{
153153
dlhandle h = nullptr;
154154

155+
#if defined(__linux__) && defined(RTLD_NODELETE)
156+
constexpr int dlopen_flags = RTLD_NOW | RTLD_NODELETE;
157+
#else
158+
constexpr int dlopen_flags = RTLD_NOW;
159+
#endif
160+
155161
// If the caller already supplied a path (has a slash) just try it.
156-
if (lib.find('/') != std::string::npos) { h = dlopen(lib.c_str(), RTLD_NOW); }
162+
if (lib.find('/') != std::string::npos) { h = dlopen(lib.c_str(), dlopen_flags); }
157163
else {
158164
// 1. Try the file in the current working directory.
159165
std::string cwdPath = "./" + lib;
160-
h = dlopen(cwdPath.c_str(), RTLD_NOW);
166+
h = dlopen(cwdPath.c_str(), dlopen_flags);
161167

162168
// 2. Fallback to the normal search path so LD_LIBRARY_PATH,
163169
// RPATH/RUNPATH, system dirs, etc. are still honoured.
164-
if (!h) { h = dlopen(lib.c_str(), RTLD_NOW); }
170+
if (!h) { h = dlopen(lib.c_str(), dlopen_flags); }
165171
}
166172
return h; // may be nullptr – caller should check and use dlerror()
167173
}

gstreamer/gstreamer.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -614,6 +614,7 @@ namespace gstreamer {
614614
* \return Shared pointer that should be retained until threaded streaming is complete.
615615
*/
616616
inline std::shared_ptr<const gstreamersMap> preloadGStreamerPlugins(const std::shared_ptr<GOptions>& gopts) {
617+
if (gstreamer::getGStreamerDefinition(gopts).empty()) { return std::make_shared<gstreamersMap>(); }
617618
return gstreamersMapPtr(gopts);
618619
}
619620

releases/0.2.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,4 +82,6 @@ Both x86_64 and ARM64 platforms are supported.
8282
- Using $SIM_HOME instead of $GEMC for installation paths
8383
- Pre-load streamer plugins before Geant4 creates worker threads
8484
- gemc.cc preloads only whatever streamers are actually configured.
85+
- Temporarely protecting, on linux, explicit G4VisExecutive teardown. This is a possible
86+
Geant4 bug: a double-free in a process-wide G4Colour map during static destruction
8587
- Various code cleanups

0 commit comments

Comments
 (0)