Global mutable variable question

Hi everyone,

I'm trying to use a global mutable variable but I can't get.
I am forced to use it because i also have a signal handler (sigaction) to which, as you know, cannot pass parameters.
I saw some example with lazy_static which always use a mutex and in my case is not good.
I tried to use an Option but i cannot get is as mutable.
Could you provide me any example or (maybe) another way to do that?
Thanks in advance.

An atomic type might suit. However, it'll be easier to answer correctly if you tell us more about your use case. What signal are you handling, and what do you want to do when it arrives? What is your program doing while this is happening?

The ctrlc library might be interesting, as it provides a safe wrapper for SIGINT handling.

My signal handler handles SIGCHLD and via siginfo i can get all data about signal, pid etc etc.
These data must be write in this global variable.
Because it is a simply struct, shoud i use AtomicPtr?
Any example?

No, AtomicPtr is for when you want to swap out pointers atomically.

How do you want to store this information (a Vec, etc.) -- and what types do they have?

Generally, this is pretty tricky, since an interrupt is able to interrupt anything happening on the main thread. You may be better off using a channel.

1 Like

exactly! There is a vector of Process (Struct).
Basically, when a signal is here, i must write the data in Process struct. Very simple.

You should use a Mutex in your static, or give a detailed description of exactly why you can't do that.

2 Likes

I'm declaring my global variable in this way:

lazy_static! {
    pub static ref UNITD_DATA: Mutex<Box<UnitdData>> = Mutex::new(Box::new(UnitdData::new()));
}

When the program starts, I get it so:

let mut unitd_data = UNITD_DATA.lock().unwrap();

UnitdData is a simple struct which contain vectors, booleans etc etc.
I can write into it without problems.
Now, i cannot pass to signal handler this variable which signature is:

extern "C" fn handle(_: libc::c_int, _: *mut libc::siginfo_t, _: *mut libc::c_void) {
}

UNITD_DATA will be locked from start to end, i am not able to acquire the lock wherever.
Moreover, use a mutex in a signal handler is a bad idea. The signal can generate theirself in whatever moment taking ownership the current thread, thus, i cannot acquire the lock.
I must globally access to UNITD_DATA as mutable without a mutex.
How could i do?

Not sure if it's the right thing to do in signal handlers, but std::sync::mpsc::Sender::send never blocks:

This method will never block the current thread.

You could perhaps use that to send an owned value (maybe one that doesn't allocate?) to another thread. That other thread could then acquire a lock or do other things which may depend on the signal handler having returned.


P.S.: Another alternative would be std::sync::mpsc::SyncSender::try_send if you are worried about the channel growing too big (or growing at all).

1 Like

Yeah, actually i have already resolved it just right with a channel.
At this point, another thread will read from the channel the signal information (sig_num, sig_code, sig_pid) and will select the process according the received pid and will restart it if the latter has a channel (Restart property).

Which channel did you use? I wonder if it's okay to allocate memory during the execution of the signal handler, for example.

My problem was that cannot access to a static global mutable variable from signal handler but from a static channel is possible instead. I used a crossbeam channel but probably a standard library's channel can is good as well.

A similar thing:

lazy_static! {
    pub static ref PIPE: (Sender<()>, Receiver<()>) = channel::unbounded();
}

My question is:

  • Does an unbounded channel allocate memory? I think it has to. And:
    • Is allocating memory okay to do in a signal handler, or will it cause race conditions / UB if the signal handler is called in a wrong moment?

Moreover, an unbounded channel might lead to memory exhaustion if your signals are generated faster than the thread(s) can consume the items from the channel. But not sure if that's a problem.

1 Like

Yes, it might choose to allocate. In the extreme case, consider what happens if the main thread becomes deadlocked— The channel will need to collect arbitrarily many signal activations to report later, because it has no way of knowing about the deadlock.

If that's not acceptable, you'll need to use some kind of bounded solution that either drops or consolidates signal information if you receive signals too fast. The simplest approach for this would be to use a bounded channel, and send() will return Err(...) if the channel is full.

Of course, I'll try to send the data before via try_send(). If the channel is full then will trace the error into system log or could re-attempt to do (for example max 3 times) after a little timeout (1 second) which should be enough to empty the channel.
You consider that these data (just three int value) will be read and rewrite into specific process channel if the process has been configured as restartable. It will be an operation very fast.
Thanks for collaboration.

I think an unbounded channel is never reported as full. It can be only disconnected.

You mentioned channel::unbounded here, which is why I was wondering / worrying whether it would be okay in your use-case.

Good! I can quietly avoid that above said even if a simple check is not a bad idea. :+1:
I have just re-check the api crossbeam::channel - Rust.
I should be ok.
Thanks again.

Not sure what you mean with a simple check. Note that there is still the potential issue of allocating, where I'm not sure if it's okay to allocate in a signal handler.

No, i mean evaluating the result of send() anyway and trace the error into system log. Nothing else

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.