How can I set a variable of type long (on 64 bit machine = 8 bytes) inside a signal handler? I've read that you can only use variables of type sig_atomic_t, which is actually implemented as volatile int inside a signal handler and it is unsafe to modify data types bigger than an int.
You can use a long inside a signal handler, you can use anything, in fact. The only thing you should take care of is proper synchronization in order to avoid race conditions.
sig_atomic_t should be used for variables shared between the signal handler and the rest of the code. Any variable "private" to the signal handler can be of any type, any size.
Sample code :
#include <signal.h>
static volatile long badShared; // NOT OK: shared not sig_atomic_t
static volatile sig_atomic_t goodShared; // OK: shared sig_atomic_t
void handler(int signum)
{
int localInt = 17;
long localLong = 23; // OK: not shared
if (badShared == 0) // NOT OK: shared not sig_atomic_t
++badShared;
if (goodShared == 0) // OK: shared sig_atomic_t
++goodShared;
}
int main()
{
signal(SOMESIGNAL, handler);
badShared++; // NOT OK: shared not sig_atomic_t
goodShared++; // OK: shared sig_atomic_t
return 0;
}
If you want to use a shared variable other than sig_atomic_t use atomics (atomic_long_read, atomic_long_set).
Related
I know there is a lot of confusion regarding volatile.
So I have 3 real life examples I'm not sure about the correct usage of volatile.
1) DMA Stream
The hardware writes directly to data with DMA.
Is volatile needed in this span?
#include <cstdint>
#include <semaphore>
#include <span>
static std::binary_semaphore semaphore{0};
//DMA Interrupt after complete receive
extern "C" void DMAcomplete() {
semaphore.release();
}
void readFromDMA(std::span<volatile uint8_t> data) {
//Modify DMA register and start DMA
//... = data.data();
//wait for DMA to finish
semaphore.acquire();
}
2) ISR read
This example is similar to the first one, but now the ISR is actually manipulating the data.
Is volatile needed in the span?
#include <cstdint>
#include <semaphore>
#include <span>
#include <atomic>
static std::binary_semaphore semaphore{0};
static std::atomic<volatile uint8_t*> data;
static std::atomic_size_t size;
static std::atomic_size_t index;
//Interrupt is called per byte
extern "C" void ISRperByte() {
uint8_t receivedData;
data[index++] = receivedData;
//Receive complete
if(index >= size-1)
semaphore.release();
}
void readFromISR(std::span<volatile uint8_t> toRead) {
data = toRead.data();
size = toRead.size();
index = 0;
//Enable Interrupt etc.
//Wait until all reads are done
semaphore.acquire();
}
3) ISR Callback
Does ICallback* have to be volatile?
#include <atomic>
class ICallback {
public:
virtual ~ICallback() = default;
virtual void doStuff() volatile = 0;
};
static std::atomic<volatile ICallback*> atomicCallback = nullptr;
//Interrupt is called by hardware
extern "C" void ISR() {
auto cb = atomicCallback.load();
if(cb)
cb->doStuff();
}
void setCallback(ICallback& cb) {
atomicCallback = &cb;
}
void resetCallback() {
atomicCallback = nullptr;
}
EDIT:
Here is s snippet of case 1 using C
#include <stdint.h>
#include <stdbool.h>
//Assume assigment to this is atomic
static volatile bool semaphore;
//DMA Interrupt after complete receive
void DMAcomplete() {
//notify
semaphore = 1;
}
void readFromDMA(volatile uint8_t* data, uint32_t size) {
semaphore = 0;
//Modify DMA register and start DMA
//... = data.data();
//wait for DMA to finish
while(semaphore != 1);
}
EDIT2:
If I call readfromDMA or readfromISR with none volatile data, is it valid after the function returns? Since the caller doesn't declare his data as volatile, but the data is changed during DMA/ISR, it seems a little suspicious.
Our compiler says this about volatile declared Objects:
● All accesses are preserved
● All accesses are complete, that is, the whole object is accessed
● All accesses are performed in the same order as given in the abstract machine
● All accesses are atomic, that is, they cannot be interrupted.
The compiler adheres to these rules for accesses to all 8-, 16-, and 32-bit scalar types.
For all combinations of object types not listed, only the rule that states that all accesses
are preserved applies.
So for the examples the following would be correct:
DMA
Volatile is not needed in readFromDMA.
BUT the data which is passed to this function has to be volatile for example std::vector<volatile uint8_t>.
ISR read
In this example the volatile in data is indeed needed, because the ISR manipulates it. Additionally, as in example 1, volatile is also needed in the declaration of the buffer for example std::vector<volatile uint8_t>.
ISR Callback
ICallback* does not need to be volatile, BUT the object implementing doStuff has to be aware, that this function is called from an ISR. So it may need some volatile member.
Example
#include <csignal>
#include <cstdio>
#include <cstdlib>
volatile std::sig_atomic_t gSignalStatus = 0;
void signal_handler(int signal) {
gSignalStatus = signal;
}
int main() {
// Install a signal handler
std::signal(SIGTERM, signal_handler);
while (gSignalStatus == 0) {}
printf("%d\n", gSignalStatus);
}
I am trying to understand few things:
Is it even correct to declare a variable with a type of std::sig_atomic_t without volatile? Given the variable is shared between the handler and a thread -- just like gSignalStatus above but without volatile. According to the answer of this post, it seems volatile is required.
According to C++ standard:
extern "C" void atomic_signal_fence(memory_order order) noexcept;
6 Effects: Equivalent to atomic_thread_fence(order), except that the resulting ordering constraints
are established only between a thread and a signal handler executed in the same thread.
it sounds like the specification assumes the signal handler can be called by any thread and so atomic_signal_fence is added to the spec? But the quoted spec above also mentions "same thread". Hence, I am confused. Tho, on my machine, the signal handler is called by the main thread. An example to illustrate the need of atomic_signal_fence would be nice too!
Thanks!
In your particular use case it may be more power efficient and appropriate to use sigwait function. No volatile or std::atomic is necessary.
It is said volatile is needed for signal handler, e.g.,
volatile int flag = 1; // volatile is needed here?
void run() {
while(flag) { /* do someting... */ }
}
void signal_handler(int sig) {
flag = 0;
}
int main() {
signal(SIGINT, sig_handler);
run();
// ...
}
It is said volatile is often not used in multithreading. But how about the similar case like above in multithreading:
int flag = 1; // is volatile needed here?
void thread_function() {
while(flag) { /* do someting... */ }
}
int main() {
// pthread_create() to create thread_function()...
sleep(10); // let thread_function run for 10 seconds
flag = 0;
// ...
}
Should the volatile keyword be used in both cases? Are the two cases treated the same way by compiler?
The only non-local values you are allowed to modify from a signal handler are those of type volatile sig_atomic_t, and atomic types. In particular, writing to your volatile int is not allowed, and if your signal handler runs you have undefined behaviour.
The c++ standard, in [intro.execution], paragraph 6, tells:
When the processing of the abstract machine is interrupted by receipt of a signal, the values of objects which are neither
— of type volatile std::sig_atomic_t nor
— lock-free atomic objects (29.4)
are unspecified during the execution of the signal handler, and the value of any object not in either of these two categories that is modified by the handler becomes undefined.
Therefore, yes, for signal handlers, you have to use volatile std::sig_atomic_t.
volatile is used to make sure that the contents of the variable is read from its actual location (memory, in our case) rather than from a CPU register.
In other words, whenever an "outside" event might change the value of a variable, you should consider using volatile ("outside" - as in, outside the relevant code block).
In both your examples, you are using the variable as a flag to signal a change in behavior. This flag, in both examples, is controlled by events "outside" the loop that that is reviewing the flag. For this reason, both examples require the use of the volatile keyword.
It should be noted volatile does not provide thread safety for a number of reasons. To make sure an object is thread safe, read/write operations must be either protected or atomic.
I'm wondering if it's possible to use "atomic access" (C++11 feature) for multi threaded acces from within a DLL injected into another process.
For example, we have this program:
struct SSomeStructure
{
int value_a;
int value_b;
};
int main()
{
SSomeStructure some_variable;
while(!GetAsyncKeyState('Q'))//loop while Q is not pressed
{
std::cout << some_variable.value_a << some_variable.value_b;
some_variable.value_a = GetTickCount();
some_variable.value_b = -some_variable.value_a;
std::cout << some_variable.value_a << some_variable.value_b;
}
}
And the program is closed source, but I have disasemlbed the "SSomeStructure" structure:
struct SSomeStructure
{
int value_a;
int value_b;
};
Now I am writing a DLL which is going to be injected into the process, but there is nothing to hook to except std::out, so, without hooking but making another thread, to safely access/modify those variables, they would need to be atomic, right?
So I was thinking to create this in my (pseudocode) DLL:
#include <atomic>
#include <thread>
//disasembled structure, but added syd::atomic
struct SSomeStructure
{
std::atomic<int> value_a;
std::atomic<int> value_b;
};
//start thread when dll loads etc...
void SomeThread()
{
SSomeStructure * some_variable = ObtainAddressOfSomeVariableFromHostProcess();
while(true)
{
some_variable->value_a = 1337;//is this now an atomic operation which is thread-safe?
}
}
Would the std::atomic in my "hack" dll which runs it's own thread, give safe multi-threaded access to global variables in the host process? (because the DLL is accessing the value atomicly in the host process?)
Formally, this program exhibits undefined behavior; you are accessing an object of one type through a pointer to another, unrelated type.
It won't work in practice either. It takes two to tango: for two threads to observe a consistent state of a scalar object, both threads must use atomic instructions to access and modify it. Think about it this way: you may take a lock before you increment a counter, but it won't do you any good if another thread increments said counter without first taking the same lock.
In the limit, the host process might have loaded the value of some_variable->value_a into a register and is not observing any of your modifications at all.
The code I'm working with has a shared signal handler that switches on the signal number to handle it appropriately.
I'm adding a custom signal. Something like this
static void signal_handler (int s)
{
if ( s == SIGTERM ) clean_up () ;
else if ( s == SIGRTMIN+1 ) ; // do nothing
}
SIGRTMIN and SIGRTMAX are #defines of function calls which initialize static data (in the implementations I've seen on google code search)
Signal handlers are supposed to be non-reentrant. Does the use of static data in the accessor to SIGRTMIN and SIGRTMAX make these macros unsafe to use in a signal handler?
I don't know what implementation you are smoking, but in libc those functions seem to simply return a constant static variable most of the time.
You are right, there is a possible race between the two calls to init(), but that simply just initializes a static int twice to the same constant, hardly a worry.
And, while the static variable is not really that constant, they tell you to only modify said variable at the start of your program(and I think only pthread really modifies it that much).
So no need to worry about these functions(from allocrtsig.c glibc 2.14).
And, if you are really worried, just call SIGRTMIN once before you bind the signal handler. That will get the init() function out of the way.
/* Return number of available real-time signal with highest priority. */
int __libc_current_sigrtmin (void)
{
#ifdef __SIGRTMIN
if (!initialized)
init ();
#endif
return current_rtmin;
}
libc_hidden_def (__libc_current_sigrtmin)
/* Return number of available real-time signal with lowest priority. */
int __libc_current_sigrtmax (void)
{
#ifdef __SIGRTMIN
if (!initialized)
init ();
#endif
return current_rtmax;
}
libc_hidden_def (__libc_current_sigrtmax)