diff --git a/DDCore/include/DD4hep/SignalHandler.h b/DDCore/include/DD4hep/SignalHandler.h
new file mode 100644
index 0000000000000000000000000000000000000000..383bb8b11b9d12d78f6692f5851bad6e40fea76f
--- /dev/null
+++ b/DDCore/include/DD4hep/SignalHandler.h
@@ -0,0 +1,47 @@
+//==========================================================================
+//  AIDA Detector description implementation 
+//--------------------------------------------------------------------------
+// Copyright (C) Organisation europeenne pour la Recherche nucleaire (CERN)
+// All rights reserved.
+//
+// For the licensing terms see $DD4hepINSTALL/LICENSE.
+// For the list of contributors see $DD4hepINSTALL/doc/CREDITS.
+//
+// Author     : M.Frank
+//
+//==========================================================================
+#ifndef DD4HEP_DDCORE_SIGNALHANDLER_H
+#define DD4HEP_DDCORE_SIGNALHANDLER_H
+
+/// System include files
+#include <csignal>
+#include <memory>
+
+/// Namespace for the AIDA detector description toolkit
+namespace dd4hep {
+
+  /// Interruptsback interface class with argument
+  /**
+   *  \author  M.Frank
+   *  \version 1.0
+   *  \ingroup DD4HEP_CORE
+   */
+  class  SignalHandler  {
+  public:
+    /// User signal handler definition
+    typedef bool (*signal_handler_t)(void* user_context, int signal);
+    /// Internal implementation class
+    class implementation;
+      
+  public:
+    /// Default constructor
+    SignalHandler();
+    /// Default destructor
+    virtual ~SignalHandler();
+    /// (Re-)apply registered interrupt handlers to override potentially later registrations by other libraries
+    void applyHandlers();
+    /// Specialized handler for any signal
+    bool registerHandler(int sig_num, void* param, signal_handler_t handler);
+  };
+}      // End namespace dd4hep
+#endif // DD4HEP_DDCORE_SIGNALHANDLER_H
diff --git a/DDCore/src/SignalHandler.cpp b/DDCore/src/SignalHandler.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..c778e3da4dc30b9a7e5fb9e951c7aafb7bea7310
--- /dev/null
+++ b/DDCore/src/SignalHandler.cpp
@@ -0,0 +1,308 @@
+//==========================================================================
+//  AIDA Detector description implementation 
+//--------------------------------------------------------------------------
+// Copyright (C) Organisation europeenne pour la Recherche nucleaire (CERN)
+// All rights reserved.
+//
+// For the licensing terms see $DD4hepINSTALL/LICENSE.
+// For the list of contributors see $DD4hepINSTALL/doc/CREDITS.
+//
+// Author     : M.Frank
+//
+//==========================================================================
+
+// Framework include files
+#include <DD4hep/Printout.h>
+#include <DD4hep/SignalHandler.h>
+
+#include <map>
+#include <string>
+#include <vector>
+#include <iostream>
+#include <unistd.h>
+#include <execinfo.h>
+
+using namespace dd4hep;
+
+using signal_handler_t = SignalHandler::signal_handler_t;
+
+namespace {
+  static bool s_exit_handler_print  = true;
+  static bool s_exit_handler_active = false;
+  static bool s_exit_handler_backtrace = false;
+  static bool s_exit_handler_sleep_on_fatal = false;
+
+  template<class T> union func_cast   {
+    void* ptr;
+    T     fun;
+    explicit func_cast(T t) { fun = t; }
+    explicit func_cast(void* t) { ptr = t; }
+  };
+}
+
+/**@class SignalHandler::implementation
+ *
+ * Small class to manipulate default signal handling
+ *
+ * \author M.Frank
+ */
+class SignalHandler::implementation {
+protected:
+  struct sig_entry_t  {
+    void* user_context { nullptr };
+    signal_handler_t user_handler { nullptr };
+  };
+  struct sig_handler_t  {
+    std::string name { };
+    struct sigaction old_action { };
+    struct sigaction handler_action { };
+    std::vector<sig_entry_t> user_handlers { };
+  };
+
+public:
+  typedef std::map<int, sig_handler_t> SigMap;
+  SigMap  m_map;
+
+public:
+  /// Default constructor
+  implementation();
+  /// Default destructor
+  ~implementation();
+  /// Singleton accessor
+  static implementation& instance();
+  /// Initialize the exit handler. Subscribe to default signals
+  void init();
+  /// Install handler for a single signal
+  void install(int num, const std::string& name, struct sigaction& action);
+  /// Subscribe to a given signal with a user context and a user handler. The context MUST be unique!
+  int subscribe(int signum, void* user_context, signal_handler_t handler);
+  /// Unsubscribe from a given signal with a user context identifier
+  int unsubscribe(int signum, void* user_context);
+  /// Create simple backtrace
+  void back_trace(int /* signum */);
+  /// Static handler callback for system signal handler
+  static void handler(int signum, siginfo_t *info,void * );
+};
+
+/// Default constructor
+SignalHandler::implementation::implementation()  {
+}
+
+/// Default destructor
+SignalHandler::implementation::~implementation()  {
+}
+
+/// Singleton accessor
+SignalHandler::implementation& SignalHandler::implementation::instance()  {
+  static std::unique_ptr<implementation> imp;
+  if ( !imp )  {
+    imp = std::make_unique<implementation>();
+  }
+  return *imp;
+}
+
+/// Initialize the exit handler. Subscribe to default signals
+void SignalHandler::implementation::init()  {
+  struct sigaction new_action;
+  sigemptyset(&new_action.sa_mask);
+  new_action.sa_handler   = 0;
+  new_action.sa_sigaction = handler;
+  new_action.sa_flags     = SA_SIGINFO;
+
+  install(SIGILL,  "SIGILL",  new_action);
+  install(SIGINT,  "SIGINT",  new_action);
+  install(SIGTERM, "SIGTERM", new_action);
+  install(SIGHUP,  "SIGHUP",  new_action);
+
+  install(SIGQUIT, "SIGQUIT", new_action);
+  install(SIGBUS,  "SIGBUS",  new_action);
+  install(SIGXCPU, "SIGXCPU", new_action);
+  sigaddset(&new_action.sa_mask,SIGSEGV);
+  sigaddset(&new_action.sa_mask,SIGABRT);
+  sigaddset(&new_action.sa_mask,SIGFPE);
+  install(SIGABRT, "SIGABRT", new_action);
+  install(SIGFPE,  "SIGFPE",  new_action);
+  install(SIGSEGV, "SIGSEGV", new_action);
+}
+
+/// Subscribe to a given signal with a user context and a user handler. The context MUST be unique!
+int SignalHandler::implementation::subscribe(int signum, void* user_context, signal_handler_t user_handler)   {
+  if ( m_map.empty() )  {
+    this->init();
+  }
+  auto ihandler = m_map.find(signum);
+  if ( ihandler == m_map.end() )   {
+    char text[32];
+    struct sigaction new_action;
+    sigemptyset(&new_action.sa_mask);
+    new_action.sa_handler   = 0;
+    new_action.sa_sigaction = SignalHandler::implementation::handler;
+    new_action.sa_flags     = SA_SIGINFO;
+    ::snprintf(text, sizeof(text),"%08X",signum);
+    install(signum, text, new_action);
+    ihandler = m_map.find(signum);
+  }
+  if ( ihandler != m_map.end() )   { // Should always be true
+    sig_entry_t entry {user_context, user_handler};
+    ihandler->second.user_handlers.emplace_back(entry);
+    return 1;
+  }
+  return 0;
+}
+
+/// Unsubscribe from a given signal with a user context identifier
+int SignalHandler::implementation::unsubscribe(int signum, void* user_context)   {
+  auto ihandler = m_map.find(signum);
+  if ( ihandler != m_map.end() )   {
+    auto & handlers = ihandler->second.user_handlers;
+    for( auto it = handlers.begin(); it != handlers.end(); ++it )   {
+      if ( it->user_context == user_context )   {
+	handlers.erase(it);
+	return 1;
+      }
+    }
+  }
+  return 0;
+}
+
+/// Create simple backtrace
+void SignalHandler::implementation::back_trace(int /* signum */) {
+  if ( s_exit_handler_backtrace )   {
+    void *bt[256];
+    char text[512];
+    int bt_size = ::backtrace(bt, sizeof(bt) / sizeof(void *));
+    size_t len = ::snprintf(text, sizeof(text), "\n[INFO] (ExitSignalHandler) %s\n",
+			    "---------------------- Backtrace ----------------------\n");
+    text[sizeof(text)-2] = '\n';
+    text[sizeof(text)-1] = 0;
+    ::write(STDERR_FILENO, text, len);
+    len = ::snprintf(text, sizeof(text), "[INFO] Number of elements in backtrace: %d\n", bt_size);
+    text[sizeof(text)-2] = '\n';
+    text[sizeof(text)-1] = 0;
+    ::write(STDERR_FILENO, text, len);
+    ::backtrace_symbols_fd(bt, bt_size, STDERR_FILENO);
+    for (int i = 0; i < bt_size; i++) {
+      len = ::snprintf(text,sizeof(text),"[INFO] (SignalHandler) %02d --> %p\n", i, bt[i]);
+      text[sizeof(text)-2] = '\n';
+      text[sizeof(text)-1] = 0;
+      ::write(STDERR_FILENO, text, len);
+    }
+  }
+}
+
+/// Install handler for a single signal
+void SignalHandler::implementation::install(int num, const std::string& name, struct sigaction& action) {
+  auto& action_entry = m_map[num];
+  int res = ::sigaction (num, &action, &action_entry.old_action);
+  if ( res != 0 ) {
+    char text[512];
+    auto len = ::snprintf(text,sizeof(text),"Failed to install exit handler for %s", name.c_str());
+    text[sizeof(text)-2] = '\n';
+    text[sizeof(text)-1] = 0;
+    ::write(STDERR_FILENO, text, len);
+    return;
+  }
+  action_entry.handler_action = action;
+  action_entry.name = name;
+}
+  
+/// Static handler callback for system signal handler
+void SignalHandler::implementation::handler(int signum, siginfo_t *info, void *ptr) {
+  SigMap& m = instance().m_map;
+  SigMap::iterator iter_handler = m.find(signum);
+  s_exit_handler_active = true;
+  if ( iter_handler != m.end() ) {
+    __sighandler_t hdlr = iter_handler->second.old_action.sa_handler;
+    func_cast<void (*)(int)> dsc0(hdlr);
+    func_cast<void (*)(int,siginfo_t*, void*)> dsc(dsc0.ptr);
+
+    if ( s_exit_handler_print ) {{
+	char text[512];
+	size_t len = ::snprintf(text,sizeof(text),
+				"[FATAL] (SignalHandler) Handle signal: %d [%s] Old action:%p Mem:%p Code:%08X\n",
+				signum,iter_handler->second.name.c_str(),dsc.ptr,info->si_addr,info->si_code);
+	text[sizeof(text)-2] = '\n';
+	text[sizeof(text)-1] = 0;
+	::write(STDERR_FILENO,text,len);
+	// Debugging hack, if enabled (default: NO)
+	if ( s_exit_handler_sleep_on_fatal )  {
+	  bool _s_sleep = true;
+	  len = ::snprintf(text,sizeof(text),
+			   "[FATAL] (SignalHandler) Sleeping for debugging.... %s\n",
+			   _s_sleep ? "YES" : "NO");
+	  text[sizeof(text)-2] = '\n';
+	  text[sizeof(text)-1] = 0;
+	  ::write(STDERR_FILENO,text,len);
+	  while ( _s_sleep ) ::usleep(100000);
+	}
+      }
+      if ( !iter_handler->second.user_handlers.empty() )    {
+	auto& handlers = iter_handler->second.user_handlers;
+	for( auto ih = handlers.rbegin(); ih != handlers.rend(); ++ih )   {
+	  if ( ih->user_handler )  {
+	    bool ret = (*(ih->user_handler))(ih->user_context, signum);
+	    if ( ret )   {
+	      return;
+	    }
+	    // Otherwise continue signal processing and eventually call default handlers
+	  }
+	  // No handler fired: call previously registered signal handler
+	  auto& entry = iter_handler->second.old_action;
+	  if ( entry.sa_handler )
+	    (*entry.sa_handler)(signum);
+	  else if ( entry.sa_sigaction )
+	    (*entry.sa_sigaction)(signum, info, ptr);
+	}
+      }
+      if ( signum == SIGSEGV || signum == SIGBUS || signum == SIGILL || signum == SIGABRT )  {
+	instance().back_trace(signum);
+      }
+      else if ( info->si_signo == SIGSEGV || info->si_signo == SIGBUS || info->si_signo == SIGILL || info->si_signo == SIGABRT )  {
+	instance().back_trace(info->si_signo);
+      }
+    }
+    if ( signum == SIGINT || signum == SIGHUP || signum == SIGFPE || signum == SIGPIPE ) {
+      if ( dsc.fun && (dsc0.fun != SIG_IGN) )
+	dsc.fun(signum, info, ptr);
+      else if ( signum == SIGHUP )
+	::_exit(signum);
+    }
+    else if ( signum == SIGSEGV && hdlr && hdlr != SIG_IGN && hdlr != SIG_DFL ) {
+      ::_exit(0);
+    }
+    else if ( hdlr && hdlr != SIG_IGN && dsc.fun )  {
+      dsc.fun(signum, info, ptr);
+    }
+    else if ( hdlr == SIG_DFL ) {
+      ::_exit(0);
+    }
+  }
+  s_exit_handler_active = false;
+}
+
+/// Default constructor
+SignalHandler::SignalHandler()
+{
+}
+
+/// Default destructor
+SignalHandler::~SignalHandler()  {
+}
+
+/// (Re-)apply registered interrupt handlers to override potentially later registrations by other libraries
+void SignalHandler::applyHandlers()  {
+  auto& imp = implementation::instance();
+  struct sigaction old_action { };
+  printout(INFO, "SignalHandler", "++ Re-apply signal handlers");
+  for( const auto& e : imp.m_map )  {
+    ::sigaction (e.first, &e.second.handler_action, &old_action);
+    printout(DEBUG, "SignalHandler",
+	     "++ Re-apply signal handler for %-10s [%3ld entries]",
+	     e.second.name.c_str(), e.second.user_handlers.size());
+  }
+}
+
+/// Install handler for any signal
+bool SignalHandler::registerHandler(int sig_num, void* param, signal_handler_t handler)  {
+  return implementation::instance().subscribe(sig_num, param, handler) == 1;
+}
diff --git a/DDG4/include/DDG4/Geant4Interrupts.h b/DDG4/include/DDG4/Geant4Interrupts.h
new file mode 100644
index 0000000000000000000000000000000000000000..2f82a80595b68adf4e1004da56e105e74228c203
--- /dev/null
+++ b/DDG4/include/DDG4/Geant4Interrupts.h
@@ -0,0 +1,55 @@
+//==========================================================================
+//  AIDA Detector description implementation 
+//--------------------------------------------------------------------------
+// Copyright (C) Organisation europeenne pour la Recherche nucleaire (CERN)
+// All rights reserved.
+//
+// For the licensing terms see $DD4hepINSTALL/LICENSE.
+// For the list of contributors see $DD4hepINSTALL/doc/CREDITS.
+//
+// Author     : M.Frank
+//
+//==========================================================================
+#ifndef DDG4_GEANT4INTERRUPTS_H
+#define DDG4_GEANT4INTERRUPTS_H
+
+/// Framework include files
+#include <DD4hep/SignalHandler.h>
+
+/// System include files
+
+/// Namespace for the AIDA detector description toolkit
+namespace dd4hep {
+
+  /// Namespace for the Geant4 based simulation part of the AIDA detector description toolkit
+  namespace sim {
+
+    /// Forward declarations
+    class Geant4Kernel;
+    
+    /// Interruptsback interface class with argument
+    /**
+     *  \author  M.Frank
+     *  \version 1.0
+     *  \ingroup DD4HEP_SIMULATION
+     */
+    class  Geant4Interrupts : public SignalHandler {
+    public:
+      /// Reference to simulation kernel
+      Geant4Kernel& m_kernel;
+      Geant4Kernel& kernel()  {  return m_kernel;  }
+
+      /// Default SIGINT handler: trigger end-of-event-loop in Geant4Kernel object
+      static bool default_sigint_handler(void* user_context, int signum);
+
+    public:
+      /// Default constructor
+      Geant4Interrupts(Geant4Kernel& krnl) : m_kernel(krnl) { }
+      /// Default destructor
+      virtual ~Geant4Interrupts() = default;
+      /// Specialized handler for SIGINT
+      bool registerHandler_SIGINT();
+    };
+  }    // End namespace sim
+}      // End namespace dd4hep
+#endif // DDG4_GEANT4INTERRUPTS_H
diff --git a/DDG4/include/DDG4/Geant4Kernel.h b/DDG4/include/DDG4/Geant4Kernel.h
index 03bfeed8d6a8cbcd04bd70f0287cb6a7634c90b4..b956f9b574407808460b499dff94bb3c0b287099 100644
--- a/DDG4/include/DDG4/Geant4Kernel.h
+++ b/DDG4/include/DDG4/Geant4Kernel.h
@@ -21,13 +21,6 @@
 #include <typeinfo>
 #include <functional>
 
-class DD4hep_End_Of_File : public std::exception {
-public:
-  DD4hep_End_Of_File() : std::exception() {}
-  virtual const char* what() const noexcept { return "Reached end of input file"; }
-
-};
-
 // Forward declarations
 class G4RunManager;
 class G4UIdirectory;
@@ -36,12 +29,30 @@ class G4VPhysicalVolume;
 /// Namespace for the AIDA detector description toolkit
 namespace dd4hep {
 
+  // Forward declarations
+  class Geant4Interrupts;
+
   /// Namespace for the Geant4 based simulation part of the AIDA detector description toolkit
   namespace sim {
 
     // Forward declarations
+    class Geant4Interrupts;
     class Geant4ActionPhase;
 
+    /// Helper class to indicate the of file
+    class DD4hep_End_Of_File : public std::exception {
+    public:
+      DD4hep_End_Of_File() : std::exception() {}
+      virtual const char* what() const noexcept { return "Reached end of input file"; }
+    };
+
+    /// Helper class to indicate the of file
+    class DD4hep_Stop_Processing : public std::exception {
+    public:
+      DD4hep_Stop_Processing() : std::exception() {}
+      virtual const char* what() const noexcept { return "Event loop STOP signalled. Processing stops"; }
+    };
+
     /// Class, which allows all Geant4Action derivatives to access the DDG4 kernel structures.
     /**
      *  To implement access to a user specified framework please see class Geant4Context.
@@ -59,6 +70,11 @@ namespace dd4hep {
       typedef std::pair<void*, const std::type_info*>   UserFramework;
       using UserCallbacks = std::vector<std::function<void()> >;
 
+      enum event_loop_status  {
+	EVENTLOOP_HALT = 0,
+	EVENTLOOP_RUNNING = 1,
+      };
+      
     protected:
       /// Reference to the run manager
       G4RunManager*      m_runManager  { nullptr };
@@ -94,14 +110,17 @@ namespace dd4hep {
       /// Property: Names with specialized factories to create G4VSensitiveDetector instances
       std::map<std::string, std::string> m_sensitiveDetectorTypes;
       /// Property: Number of events to be executed in batch mode
-      long          m_numEvent = 10;
+      long          m_numEvent       = 10;
       /// Property: Output level
-      int           m_outputLevel = 0;
+      int           m_outputLevel    = 0;
 
       /// Master property: Number of execution threads in multi threaded mode.
-      int           m_numThreads = 0;
+      int           m_numThreads     = 0;
       /// Master property: Instantiate the Geant4 scoring manager object
       int           m_haveScoringMgr = false;
+      /// Master property: Flag if event loop is enabled
+      int           m_processEvents  = EVENTLOOP_RUNNING;
+
       
       /// Registered action callbacks on configure
       UserCallbacks m_actionConfigure  { };
@@ -118,8 +137,10 @@ namespace dd4hep {
 
       /// Parent reference
       Geant4Kernel*      m_master         { nullptr };
-      Geant4Kernel*      m_shared         { nullptr };
+      /// Thread context reference
       Geant4Context*     m_threadContext  { nullptr };
+      /// Interrupt/signal handler: only on master instance
+      Geant4Interrupts*  m_interrupts     { nullptr };
 
       bool isMaster() const  { return this == m_master; }
       bool isWorker() const  { return this != m_master; }
@@ -136,9 +157,6 @@ namespace dd4hep {
       /// Thread's master context
       Geant4Kernel& master()  const  { return *m_master; }
 
-      /// Shared action context
-      Geant4Kernel& shared()  const  { return *m_shared; }
-
       //bool isMultiThreaded() const { return m_multiThreaded; }
       bool isMultiThreaded() const { return m_numThreads > 0; }
 
@@ -248,6 +266,19 @@ namespace dd4hep {
       /// Register terminate callback. Signature:   (function)()
       void register_terminate(const std::function<void()>& callback);
 
+      /// Access interrupt handler. Will be created on the first call
+      Geant4Interrupts& interruptHandler()  const;
+      /// Trigger smooth end-of-event-loop with finishing currently processing event
+      void triggerStop();
+      /// Check if event processing should be continued
+      bool processEvents()  const;
+      /// Install DDG4 default handler for a given signal. If no handler: return false
+      bool registerInterruptHandler(int sig_num);
+      /// (Re-)apply registered interrupt handlers to override potentially later registrations by other libraries
+      /** In this case we overwrite signal handlers applied by Geant4.
+       */
+      void applyInterruptHandlers();
+      
       /// Register action by name to be retrieved when setting up and connecting action objects
       /** Note: registered actions MUST be unique.
        *  However, not all actions need to registered....
diff --git a/DDG4/python/DDG4.py b/DDG4/python/DDG4.py
index 41007834e44c760ca725c32f34b198d15a7c9e5a..8c1c861d59d2362c1f821be5121c56224b819a38 100644
--- a/DDG4/python/DDG4.py
+++ b/DDG4/python/DDG4.py
@@ -10,6 +10,7 @@
 # ==========================================================================
 from __future__ import absolute_import, unicode_literals
 import logging
+import signal
 import cppyy
 from dd4hep_base import *  # noqa: F403
 
@@ -447,6 +448,17 @@ class Geant4:
     ui_name = self.master().UI
     return self.master().globalAction(ui_name)
 
+  def registerInterruptHandler(self, signum=signal.SIGINT):
+    """
+    Enable interrupt handling: smooth handling of CTRL-C
+      - Finish processing of the current event(s)
+      - Drain the event loop
+      - Properly finalyze the job
+
+    \author  M.Frank
+    """
+    return self.master().registerInterruptHandler(signum)
+
   def addUserInitialization(self, worker, worker_args=None, master=None, master_args=None):
     """
     Configure Geant4 user initialization for optionasl multi-threading mode
diff --git a/DDG4/src/Geant4Exec.cpp b/DDG4/src/Geant4Exec.cpp
index e1aa5e6fc652104185aeb21fb41cd1a3ee30f4ac..de9fbf9428fa7e6ac69527660a77400ad3c01190 100644
--- a/DDG4/src/Geant4Exec.cpp
+++ b/DDG4/src/Geant4Exec.cpp
@@ -375,6 +375,7 @@ namespace dd4hep {
       createClientContext(run);
       kernel().executePhase("begin-run",(const void**)&run);
       if ( m_sequence ) m_sequence->begin(run); // Action not mandatory
+      kernel().applyInterruptHandlers();
     }
 
     /// End-of-run callback
@@ -624,6 +625,7 @@ int Geant4Exec::initialize(Geant4Kernel& kernel) {
   ///
   /// Initialize G4 engine
   ///
+  kernel.applyInterruptHandlers();
   kernel.executePhase("initialize",0);
   runManager.Initialize();
   return 1;
@@ -649,6 +651,7 @@ int Geant4Exec::run(Geant4Kernel& kernel) {
     throw std::runtime_error(format("Geant4Exec","++ Failed to locate UI interface %s.",value.c_str()));
   }
   long nevt = kernel.property("NumEvents").value<long>();
+  kernel.applyInterruptHandlers();
   kernel.runManager().BeamOn(nevt);
   kernel.executePhase("stop",0);
   return 1;
diff --git a/DDG4/src/Geant4GeneratorAction.cpp b/DDG4/src/Geant4GeneratorAction.cpp
index df31ea3d8e245177b1eea5162c255ee3476f3857..dcec7b8fc0f6cc7ea6634ab01a1de0e1be2de1f0 100644
--- a/DDG4/src/Geant4GeneratorAction.cpp
+++ b/DDG4/src/Geant4GeneratorAction.cpp
@@ -119,6 +119,10 @@ void Geant4GeneratorActionSequence::adopt(Geant4GeneratorAction* action) {
 
 /// Generator callback
 void Geant4GeneratorActionSequence::operator()(G4Event* event) {
-  m_actors(&Geant4GeneratorAction::operator(), event);
-  m_calls(event);
+  if ( context()->kernel().processEvents() )  {
+    m_actors(&Geant4GeneratorAction::operator(), event);
+    m_calls(event);
+    return;
+  }
+  throw DD4hep_Stop_Processing();
 }
diff --git a/DDG4/src/Geant4Interrupts.cpp b/DDG4/src/Geant4Interrupts.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..5e593edc36e1865fb40fcc582623468648bdfb1f
--- /dev/null
+++ b/DDG4/src/Geant4Interrupts.cpp
@@ -0,0 +1,33 @@
+//==========================================================================
+//  AIDA Detector description implementation 
+//--------------------------------------------------------------------------
+// Copyright (C) Organisation europeenne pour la Recherche nucleaire (CERN)
+// All rights reserved.
+//
+// For the licensing terms see $DD4hepINSTALL/LICENSE.
+// For the list of contributors see $DD4hepINSTALL/doc/CREDITS.
+//
+// Author     : M.Frank
+//
+//==========================================================================
+
+// Framework include files
+#include <DD4hep/Printout.h>
+#include <DDG4/Geant4Kernel.h>
+#include <DDG4/Geant4Interrupts.h>
+
+/// Default SIGINT handler: trigger end-of-event-loop in Geant4Kernel object
+bool dd4hep::sim::Geant4Interrupts::default_sigint_handler(void* user_context, int)   {
+  Geant4Kernel* krnl = (Geant4Kernel*)user_context;
+  if ( krnl )  {
+    krnl->triggerStop();
+    return true;
+  }
+  except("Geant4Interrupts", "+++ Internal error: no user context in default SIGINT handler!");
+  return true;
+}
+
+/// Install specialized handler for SIGINT
+bool dd4hep::sim::Geant4Interrupts::registerHandler_SIGINT()  {
+  return this->registerHandler(SIGINT, &m_kernel, default_sigint_handler);
+}
diff --git a/DDG4/src/Geant4Kernel.cpp b/DDG4/src/Geant4Kernel.cpp
index dc85386dc6f8577a127fb8e7109d8a2c88a15f6e..27e789c1f277118fbbea00a045b2513b433aae12 100644
--- a/DDG4/src/Geant4Kernel.cpp
+++ b/DDG4/src/Geant4Kernel.cpp
@@ -21,6 +21,7 @@
 
 #include <DDG4/Geant4Kernel.h>
 #include <DDG4/Geant4Context.h>
+#include <DDG4/Geant4Interrupts.h>
 #include <DDG4/Geant4ActionPhase.h>
 
 // Geant4 include files
@@ -33,12 +34,14 @@
 // C/C++ include files
 #include <algorithm>
 #include <pthread.h>
+#include <csignal>
 #include <memory>
 
 using namespace dd4hep::sim;
 
 namespace {
-  G4Mutex kernel_mutex=G4MUTEX_INITIALIZER;
+
+  G4Mutex kernel_mutex = G4MUTEX_INITIALIZER;
   std::unique_ptr<Geant4Kernel> s_main_instance;
   void description_unexpected()    {
     try  {
@@ -98,11 +101,11 @@ Geant4Kernel::Geant4Kernel(Detector& description_ref)
   declareProperty("SensitiveTypes",       m_sensitiveDetectorTypes);
   declareProperty("RunManagerType",       m_runManagerType = "G4RunManager");
   declareProperty("DefaultSensitiveType", m_dfltSensitiveDetectorType = "Geant4SensDet");
+  m_interrupts = new Geant4Interrupts(*this);
   m_controlName = "/ddg4/";
   m_control = new G4UIdirectory(m_controlName.c_str());
   m_control->SetGuidance("Control for named Geant4 actions");
   setContext(new Geant4Context(this));
-  //m_shared = new Geant4Kernel(description_ref, this, -2);
   InstanceCount::increment(this);
 }
 
@@ -120,9 +123,9 @@ Geant4Kernel::Geant4Kernel(Geant4Kernel* krnl, unsigned long ident)
   m_sensitiveDetectorTypes      = m_master->m_sensitiveDetectorTypes;
   m_dfltSensitiveDetectorType   = m_master->m_dfltSensitiveDetectorType;
   declareProperty("UI",m_uiName = m_master->m_uiName);
-  declareProperty("OutputLevel", m_outputLevel = m_master->m_outputLevel);
-  declareProperty("OutputLevels",m_clientLevels = m_master->m_clientLevels);
-  ::snprintf(text,sizeof(text),"/ddg4.%d/",(int)(m_master->m_workers.size()));
+  declareProperty("OutputLevel",  m_outputLevel  = m_master->m_outputLevel);
+  declareProperty("OutputLevels", m_clientLevels = m_master->m_clientLevels);
+  ::snprintf(text, sizeof(text), "/ddg4.%d/", (int)(m_master->m_workers.size()));
   m_controlName = text;
   m_control = new G4UIdirectory(m_controlName.c_str());
   m_control->SetGuidance("Control for thread specific Geant4 actions");
@@ -139,6 +142,7 @@ Geant4Kernel::~Geant4Kernel() {
   if ( isMaster() )  {
     detail::releaseObjects(m_globalFilters);
     detail::releaseObjects(m_globalActions);
+    detail::deletePtr(m_interrupts);
   }
   destroyPhases();
   detail::deletePtr(m_runManager);
@@ -169,6 +173,40 @@ Geant4Kernel& Geant4Kernel::instance(Detector& description) {
   return *(s_main_instance.get());
 }
 
+/// Access interrupt handler. Will be created on the first call
+Geant4Interrupts& Geant4Kernel::interruptHandler()  const  {
+  if ( isMaster() )
+    return *this->m_interrupts;	
+  return this->m_master->interruptHandler();
+}
+
+/// Trigger smooth end-of-event-loop with finishing currently processing event
+void Geant4Kernel::triggerStop()  {
+  printout(INFO, "Geant4Kernel",
+	   "+++ Stop signal seen. Will finish after current event(s) have been processed.");
+  printout(INFO, "Geant4Kernel",
+	   "+++ Depending on the complexity of the simulation, this may take some time ...");
+  this->m_master->m_processEvents = EVENTLOOP_HALT;
+}
+
+/// Access flag if event loop is enabled
+bool Geant4Kernel::processEvents()  const  {
+  return this->m_master->m_processEvents == EVENTLOOP_RUNNING;
+}
+
+/// Install DDG4 default handler for a given signal. If no handler: return false
+bool Geant4Kernel::registerInterruptHandler(int sig_num)   {
+  if ( sig_num == SIGINT )  {
+    return interruptHandler().registerHandler_SIGINT();
+  }
+  return false;
+}
+
+/// (Re-)apply registered interrupt handlers to override potentially later registrations by other libraries
+void Geant4Kernel::applyInterruptHandlers()  {
+  interruptHandler().applyHandlers();
+}
+
 /// Access thread identifier
 unsigned long int Geant4Kernel::thread_self()    {
   unsigned long int thr_id = (unsigned long int)::pthread_self();
@@ -181,7 +219,7 @@ Geant4Kernel& Geant4Kernel::createWorker()   {
     unsigned long identifier = thread_self();
     Geant4Kernel* w = new Geant4Kernel(this, identifier);
     m_workers[identifier] = w;
-    printout(INFO,"Geant4Kernel","+++ Created worker instance id=%ul",identifier);
+    printout(INFO, "Geant4Kernel", "+++ Created worker instance id=%ul",identifier);
     return *w;
   }
   except("Geant4Kernel", "DDG4: Only the master instance may create workers.");
@@ -243,10 +281,10 @@ void Geant4Kernel::defineSensitiveDetectorType(const std::string& type, const st
 }
 
 void Geant4Kernel::printProperties()  const  {
-  printout(ALWAYS,"Geant4Kernel","OutputLevel:  %d", m_outputLevel);
-  printout(ALWAYS,"Geant4Kernel","UI:           %s", m_uiName.c_str());
-  printout(ALWAYS,"Geant4Kernel","NumEvents:    %ld",m_numEvent);
-  printout(ALWAYS,"Geant4Kernel","NumThreads:   %d", m_numThreads);
+  printout(ALWAYS,"Geant4Kernel","OutputLevel:  %d",  m_outputLevel);
+  printout(ALWAYS,"Geant4Kernel","UI:           %s",  m_uiName.c_str());
+  printout(ALWAYS,"Geant4Kernel","NumEvents:    %ld", m_numEvent);
+  printout(ALWAYS,"Geant4Kernel","NumThreads:   %d",  m_numThreads);
   for( const auto& [name, level] : m_clientLevels )
     printout(ALWAYS,"Geant4Kernel","OutputLevel[%s]:  %d", name.c_str(), level);
 }
diff --git a/DDG4/src/Geant4UIManager.cpp b/DDG4/src/Geant4UIManager.cpp
index b4bb2c3c3891eb6d15669f5f86d579e0b47c641f..8c4081bbb557ae5381843a6f41fc6725f0053888 100644
--- a/DDG4/src/Geant4UIManager.cpp
+++ b/DDG4/src/Geant4UIManager.cpp
@@ -251,7 +251,8 @@ void Geant4UIManager::start() {
   info("++ Start run with %d events.",numEvent);
   try {
     context()->kernel().runManager().BeamOn(numEvent);
-  } catch (DD4hep_End_Of_File& e) {
+  }
+  catch (DD4hep_End_Of_File& e) {
     info("++ End of file reached, ending run...");
     context()->kernel().runManager().RunTermination();
   }
diff --git a/examples/ClientTests/scripts/SiliconBlock.py b/examples/ClientTests/scripts/SiliconBlock.py
index 18af99420f2b82428a6cd6c4ae572598dd915881..d77c9c930403e138428fa127ced2a3841c382964 100644
--- a/examples/ClientTests/scripts/SiliconBlock.py
+++ b/examples/ClientTests/scripts/SiliconBlock.py
@@ -36,6 +36,7 @@ def run():
 
   DDG4.importConstants(kernel.detectorDescription(), debug=False)
   geant4 = DDG4.Geant4(kernel, tracker='Geant4TrackerCombineAction')
+  geant4.registerInterruptHandler()
   geant4.printDetectors()
   # Configure UI
   if args.macro:
diff --git a/examples/DDG4/CMakeLists.txt b/examples/DDG4/CMakeLists.txt
index 94fdc6fdeaef507f8e484d17df4ee4cf24736a8e..e447f6642f4440e5f5e617e43f8f5a005e2dd538 100644
--- a/examples/DDG4/CMakeLists.txt
+++ b/examples/DDG4/CMakeLists.txt
@@ -112,4 +112,12 @@ if (DD4HEP_USE_GEANT4)
     REGEX_FAIL " ERROR ;EXCEPTION;Exception"
   )
   #
+  # Test G4 SIGINT handler
+  dd4hep_add_test_reg( DDG4_SIGINT_handler
+    COMMAND    "${CMAKE_INSTALL_PREFIX}/bin/run_test_DDG4.sh"
+    EXEC_ARGS  ${Python_EXECUTABLE} ${DDG4examples_INSTALL}/scripts/TestSIGINT.py
+    REGEX_PASS "Event loop STOP signalled. Processing stops"
+    REGEX_FAIL " ERROR ;EXCEPTION"
+  )
+  #
 endif()
diff --git a/examples/DDG4/scripts/TestSIGINT.py b/examples/DDG4/scripts/TestSIGINT.py
new file mode 100644
index 0000000000000000000000000000000000000000..567c087f7b88b5b619591e03dc697f0435ba16d3
--- /dev/null
+++ b/examples/DDG4/scripts/TestSIGINT.py
@@ -0,0 +1,88 @@
+# ==========================================================================
+#  AIDA Detector description implementation
+# --------------------------------------------------------------------------
+# Copyright (C) Organisation europeenne pour la Recherche nucleaire (CERN)
+# All rights reserved.
+#
+# For the licensing terms see $DD4hepINSTALL/LICENSE.
+# For the list of contributors see $DD4hepINSTALL/doc/CREDITS.
+#
+# ==========================================================================
+#
+from __future__ import absolute_import, unicode_literals
+import logging
+#
+logging.basicConfig(format='%(levelname)s: %(message)s', level=logging.INFO)
+logger = logging.getLogger(__name__)
+#
+#
+"""
+
+   dd4hep simulation example setup using the python configuration
+
+"""
+
+
+def run():
+  import os
+  import DDG4
+  from DDG4 import OutputLevel as Output
+  from g4units import GeV, keV
+
+  kernel = DDG4.Kernel()
+  install_dir = os.environ['DD4hepExamplesINSTALL']
+  kernel.loadGeometry(str("file:" + install_dir + "/examples/ClientTests/compact/SiliconBlock.xml"))
+
+  DDG4.importConstants(kernel.detectorDescription(), debug=False)
+  geant4 = DDG4.Geant4(kernel, tracker='Geant4TrackerCombineAction')
+  geant4.registerInterruptHandler()
+  geant4.printDetectors()
+  # Configure UI
+  geant4.setupUI(typ="tcsh", vis=False, macro=None, ui=False)
+
+  # Configure field
+  geant4.setupTrackingField(prt=True)
+  # Configure Event actions
+  prt = DDG4.EventAction(kernel, 'Geant4ParticlePrint/ParticlePrint')
+  prt.OutputLevel = Output.DEBUG
+  prt.OutputType = 3  # Print both: table and tree
+  kernel.eventAction().adopt(prt)
+
+  generator_output_level = Output.INFO
+
+  # Configure G4 geometry setup
+  seq, act = geant4.addDetectorConstruction("Geant4DetectorGeometryConstruction/ConstructGeo")
+  act.DebugMaterials = True
+  act.DebugElements = False
+  act.DebugVolumes = True
+  act.DebugShapes = True
+  act.DebugSurfaces = True
+
+  # Setup particle gun
+  gun = geant4.setupGun("Gun", particle='gamma', energy=1 * GeV, multiplicity=1)
+  gun.direction = (0.0, 0.0, 1.0)
+  gun.OutputLevel = generator_output_level
+  kernel.NumEvents = 10
+
+  act = DDG4.EventAction(kernel, 'TestSignalAction/SigAction', True)
+  act.signal_event = 3
+  kernel.eventAction().add(act)
+  
+  # And handle the simulation particles.
+  part = DDG4.GeneratorAction(kernel, "Geant4ParticleHandler/ParticleHandler")
+  kernel.generatorAction().adopt(part)
+  part.SaveProcesses = ['conv', 'Decay']
+  part.MinimalKineticEnergy = 1 * keV
+  part.KeepAllParticles = False
+  part.PrintEndTracking = True
+  part.enableUI()
+
+  # Now build the physics list:
+  phys = geant4.setupPhysics('QGSP_BERT')
+  phys.dump()
+  # Start the engine...
+  geant4.execute()
+
+
+if __name__ == "__main__":
+  run()
diff --git a/examples/DDG4/src/TestSignalAction.cpp b/examples/DDG4/src/TestSignalAction.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..29f2fafe957509c0d1935815cbc0c9ebc2e34abc
--- /dev/null
+++ b/examples/DDG4/src/TestSignalAction.cpp
@@ -0,0 +1,58 @@
+//==========================================================================
+//  AIDA Detector description implementation 
+//--------------------------------------------------------------------------
+// Copyright (C) Organisation europeenne pour la Recherche nucleaire (CERN)
+// All rights reserved.
+//
+// For the licensing terms see $DD4hepINSTALL/LICENSE.
+// For the list of contributors see $DD4hepINSTALL/doc/CREDITS.
+//
+// Author     : M.Frank
+//
+//==========================================================================
+
+// Framework include files
+#include "DDG4/Geant4EventAction.h"
+
+#include <csignal>
+#include <unistd.h>
+
+/// Namespace for the AIDA detector description toolkit
+namespace dd4hep {
+
+  /// Namespace for the Geant4 based simulation part of the AIDA detector description toolkit
+  namespace sim {
+    
+    /// Class to print message for debugging
+    /** Class to print message for debugging
+     *
+     *  \author  M.Frank
+     *  \version 1.0
+     *  \ingroup DD4HEP_SIMULATION
+     */
+    class TestSignalAction : public Geant4EventAction {
+      long num_calls { 0 };
+      int  signal_event { 10000 };
+
+    public:
+      /// Standard constructor
+      TestSignalAction(Geant4Context* context, const std::string& nam)
+	: Geant4EventAction(context, nam) 
+      {
+	declareProperty("signal_event", signal_event);
+      }
+      /// Default destructor
+      virtual ~TestSignalAction() = default;
+      /// Begin-of-event callback
+      virtual void begin(const G4Event* /* event */)  {
+	if ( ++num_calls == signal_event )  {
+	  always("Sending interrupt signal to self at call %d", ++num_calls);
+	  ::kill(::getpid(), SIGINT);
+	}
+      }
+    };
+  }    // End namespace sim
+}      // End namespace dd4hep
+
+#include "DDG4/Factories.h"
+DECLARE_GEANT4ACTION_NS(dd4hep::sim,TestSignalAction)