Does LJM handle signals?
LJM catches all terminal signals, by default, in order to clean up LabJack device connections. LJM's signal handlers do the following:
Ends all device streaming and close all device connections
Optionally log which signal was received at
LJM_FATAL
priorityEssentially exit the processes by setting the signal handler to default and re-raising the signal
LJM attempts to set the signal handlers upon the first call to LJM that attempts communication. (LJM_CloseAll
is a quick way to force LJM to initialize the signal handlers.)
On Windows, signals are handled using signal. The signals that are handled are:
SIGINT
SIGILL
SIGABRT
SIGFPE
SIGSEGV
SIGTERM
On Linux/macOS, LJM uses the oldact parameter of sigaction to determine if there was previously a signal handler; if oldact
is not NULL
for a given signal, LJM will reset oldact
as the signal handler and not handle that signal. The default signals that are handled are:
SIGHUP
SIGINT
SIGQUIT
SIGILL
SIGABRT
SIGFPE
SIGBUS
SIGSEGV
SIGTERM
SIGTSTP
SIGPIPE
Custom Cleanup on Linux/macOS
If your application needs to clean up resources, you can call LJM's signal handler from your own signal handler. To do this, force LJM to initialize signal handlers by calling LJM_CloseAll
, then call sigaction
to set your handler, and finally set the previous signal handler to be called in your handler. A Linux/macOS C++ example to catch Ctrl-C follows:
...
#include <signal.h>
#include <errno.h>
static volatile void (*ljm_handler)(int) = NULL;
static int handleToCleanUp = 0;
void my_handler(int sig)
{
// printf may not be re-entrant: because of this, it typically should not be
// used in a signal handler. It is used here for ease of exposition.
printf("my_handler is handling signal: %d\n", sig);
// Perform custom cleanup behavior here
int err = LJM_eWriteName(handleToCleanUp, ...);
if (err) {
// Handle error
}
if (ljm_handler != NULL) {
printf("Calling ljm_handler from my_handler...\n");
ljm_handler(sig);
}
exit(-1);
}
int main()
{
// Initialize the LJM signal handler by "attempting" device communication
// using LJM_CloseAll - we can ignore the return value. Other functions,
// such as LJM_Open or LJM_ListAll work as well.
LJM_CloseAll();
struct sigaction act, oldact;
act.sa_handler = my_handler;
act.sa_flags = 0;
int error = sigaction(SIGINT, &act, &oldact);
if (error) {
printf("There was an error during sigaction: %d\n", errno);
exit(errno);
}
printf("oldact.sa_handler: %p\n", oldact.sa_handler);
ljm_handler = (volatile void (*)(int))oldact.sa_handler;
int handle;
error = LJM_Open(LJM_dtANY, LJM_ctANY, LJM_idANY, &handle);
if (error) {
// Handle error
}
handleToCleanUp = handle;
...
}
Note that sigprocmask
can be used to block signals while you are calling sigaction
. Also note that other signal behaviors can be implemented using sigaction
. See the man-pages for sigaction
and sigprocmask
for more details.
Custom Cleanup for ctrl+c
on Windows
If your application needs to clean up resources, you can use SetConsoleCtrlHandler
to add a handler routine. Do this after LJM's signal handlers are initialized. When ctrl+c is pressed, your handler routine is called, then LJM's signal handler is called, which exits the program.
A minimal Python script to set up a handler routine by importing Kernel32.dll
is as follows:
import ctypes
import sys
from labjack import ljm
ljm.closeAll() # Initializes LJM's signal handlers
if not sys.platform.startswith("win32"):
raise ValueError("Unsupported platform: " + sys.platform)
kernel32 = ctypes.CDLL("Kernel32.dll")
def ctrlc_handler(dwCtrlType):
print("Python ctrlc_handler called with dwCtrlType " + str(dwCtrlType))
# This is where you would close a file, etc.
ctrlc_callback_type = ctypes.WINFUNCTYPE(None, ctypes.c_int)
cc_h = ctrlc_callback_type(ctrlc_handler)
success = kernel32.SetConsoleCtrlHandler(cc_h, True)
if not success:
ValueError("SetConsoleCtrlHandler failed")
print "please press ctrl+c..."
while True:
pass