Signal handling in VisiBroker for C++ application (Part Two)

Signal handling in VisiBroker for C++ application (Part Two)

[[wiki: Traps and Pitfalls in VisiBroker Application Development |Back]]

 

In the first part of the article [[wiki: Signal handling in VisiBroker for C++ application (Part One)| Signal handling in VisiBroker for C++ application (Part One)]], we have covered the deadlock issues encountered in the traditional signal handling approach used in the single-threaded application that has been re-deployed in the modern multi-threaded application. This second part shall rectify the deadlock issues by using the relevant signal APIs that allows us to process the signals concurrently without interrupting the other worker threads that are currently running.

The POSIX-compliant Signal Handling Approach

In this section, we shall illustrate the appropriate approach for handling signals in a multi-threaded process. The following is the same illustration of a user-defined signal handling function to trap the signal “SIGTERM” to implement its cleanup process before shutting down the VisiBroker ORB. The difference now is the use of separate thread to handle the interested signal using sigwait() to listen for the signal.

 

Code sample of the preferred signal handling approach in a multi-threaded process:

#include <pthread.h>
#include <signal.h>
#include <iostream>
#include "corba.h"

using namespace std;

CORBA::ORB_var orb;
sigset_t newsigs;

extern "C" {
  void* signal_handler_thread(void *arg)
  {
    char *thread_name = (char*)arg;

    int sig = 0;
    int rc = sigwait(&newsigs, &sig);
    if (rc < 0) {
      cerr << thread_name << " - sigwait error: " << rc << endl;
    }
    cout << thread_name << " caught: " << sig << endl;
    cout << " cleaning up begins..." << endl;
    // User cleanup code here.
    cout << " cleaning up ends..." << endl;
    cout << " shutting down ORB..." << endl;
    orb->shutdown(1UL);
    cout << " ORB shut down." << endl;
    return NULL;
  }
}

int main (int argc, char** argv)
{
  sigemptyset(&newsigs);
  sigaddset(&newsigs, SIGTERM);
  pthread_sigmask(SIG_BLOCK, &newsigs, NULL);

  pthread_t tid;
  int rc = 0;
  rc = pthread_create(&tid, NULL, signal_handler_thread, "signal handler thread"
);
  if (rc != 0) {
    cerr << "pthread_create() error: " << rc << endl;
    return 1;
  }

  try {
    // Initialize the ORB.
    orb = CORBA::ORB_init(argc, argv);

    // get a reference to the root POA
    CORBA::Object_var obj = orb->resolve_initial_references("RootPOA");
    PortableServer::POA_var rootPOA = PortableServer::POA::_narrow(obj);

    CORBA::PolicyList policies;
    policies.length(1);
    policies[(CORBA::ULong)0] = rootPOA->create_lifespan_policy(
                                                PortableServer::PERSISTENT);

    // get the POA Manager
    PortableServer::POAManager_var poa_manager = rootPOA->the_POAManager();

    // Create myPOA with the right policies
    PortableServer::POA_var myPOA = rootPOA->create_POA("bank_agent_poa",
                                                        poa_manager,
                                                        policies);

    // Activate the POA Manager
    poa_manager->activate();

    cout << "Server is up and running" << endl;
    // Wait for incoming requests
    orb->run();
  }
  catch(const CORBA::Exception& e) {
    cerr << e << endl;
    return 3;
  }
  cout << "Server is exiting..." << endl;
  return 0;
}

 

 Sample process run output:

$ ./server &
[1]     5383
Server is up and running

$ kill -s term 5383
signal handler thread caught: 15
 cleaning up begins...
 cleaning up ends...
 shutting down ORB...
 ORB shut down.
Server is exiting...
[1] -  Done                    ./server &

 

From the code sample, you can see that a separate thread routine “signal_handler_thread” is created to listen for the signal that the user is interested. In this code sample, we continue to use “SIGTERM” signal for illustration.

Before we could start using the signal handling routine, there are preparation stages prior to create the signal handler thread. In a multi-threaded process, signals could deliver to any running threads in a random fashion. Such randomness causes the program logic to become unpredictable and has undesirable consequences. Any good programming practice would ensure all variables are initialized appropriately in order to have consistent runtime behaviour outcome. Likewise, good program design ensures that the signal is consistently deliver to the intended routine to avoid missed target.

In order to prevent all the other threads from receiving the interested signal and ensure that it is always delivered to the signal handling thread for its intended purpose. First, set up signal mask for a signal or a set of signals to be blocked via pthread_sigmask() in the “main” function. Any other threads created will now inherit a copy of the blocked signal mask and will now not interrupted by the signals. Next is to create a dedicated thread “signal_handler_thread” to fetch those blocked signals via sigwait(). You can see the sample run output in the shell session behaved according to the intended design.

Such an approach has the benefit that the signal handling thread is the same as all the other threads created by the application user or the VisiBroker libraries. It runs independently and concurrently. It is in the user application space, the running process will not suspended by the kernel during the signal delivery. This means that all other threads can continue to execute while the signal handling thread proceed to process the signals in a concurrent fashion. In term of CPU efficiency, it is better than the traditional approach discussed in the previous section.

All the restrictions mentioned in the traditional approach section no longer apply. The “signal_handler_thread” routine is just like any other routines running in the other user threads or the VisiBroker worker threads.

Since VisiBroker for C++ utilized this approach to handle SIGINT and SIGTERM signals to shutdown and exiting the application process, application user can turn it off to provide their own handling logic if need arise. The property "vbroker.orb.asyncSigwait=0" need to be set in order to turn off the VisiBroker signal handling thread routine.

 

[[wiki:Traps and Pitfalls in VisiBroker Application Development | Back]]

Labels (1)

DISCLAIMER:

Some content on Community Tips & Information pages is not officially supported by Micro Focus. Please refer to our Terms of Use for more detail.
Top Contributors
Version history
Revision #:
2 of 2
Last update:
‎2020-03-13 21:05
Updated by:
 
The opinions expressed above are the personal opinions of the authors, not of Micro Focus. By using this site, you accept the Terms of Use and Rules of Participation. Certain versions of content ("Material") accessible here may contain branding from Hewlett-Packard Company (now HP Inc.) and Hewlett Packard Enterprise Company. As of September 1, 2017, the Material is now offered by Micro Focus, a separately owned and operated company. Any reference to the HP and Hewlett Packard Enterprise/HPE marks is historical in nature, and the HP and Hewlett Packard Enterprise/HPE marks are the property of their respective owners.