My application has a thread for handling OS signals, so to not block the programLoop(). This thread, processOSSignals, basically keeps on reading the file descriptor for signals SIGINT, SIGTERM, SIGQUIT. On their reception, loopOver being initially true, is set to false.
int mSigDesc = -1;
void init()
{
// creates file descriptor for reading SIGINT, SIGTERM, SIGQUIT
// blocks signals with sigprocmask(SIG_BLOCK, &mask, nullptr)
...
mSigDesc = signalfd(mSigDesc, &mask, SFD_NONBLOCK); // OR 3rd param = 0?
}
void processOSSignals()
{
while (loopOver)
{
struct signalfd_siginfo fdsi;
auto readedBytes = read(mSigDesc, &fdsi, sizeof(fdsi));
...
}
}
int main()
{
init();
std::thread ossThread(processOSSignals);
programLoop();
ossThread.join();
}
My question is - should mSigDesc be set to blocking or non-blocking (asynchronous) mode?
In non-blocking mode, this thread is always busy, but inefficiently reading and returning EAGAIN over and over again.
In blocking mode, it waits until one of the signals is received, but if it is never sent, the ossThread will never join.
How should it be handled? Use sleep() in the non-blocking mode, to attempt reading only occasionally? Or maybe use select() in the blocking mode, to monitor mSigDesc and read only when sth. is available there?
Whether you use blocking or non-blocking I/O depends on how you want to handle your I/O.
Typically, if you have a single thread which is dedicated to reading from the signal file descriptor and you simply want it to wait until it gets a signal, then you should use blocking I/O.
However, in many contexts, spawning a single thread for each I/O operation is inefficient. A thread requires a stack, which may consume a couple megabytes, and it's often more efficient to process many file descriptors (which may be of many different types) by putting them all in non-blocking mode and waiting until one of them is ready.
Typically, this is done portably using poll(2). select(2) is possible, but on many systems, it is limited to a certain number of file descriptors (on Linux, 1024), and many programs will exceed that number. On Linux, the epoll(7) family of functions can also be used, and you may prefer that if you're already using such non-portable constructions as signalfd(2).
For example, you might want to handle signal FDs as part of your main loop, in which case including that FD as one the FDs that your main loop processes using poll(2) or one of the other functions might be more desirable.
What you should avoid doing is spinning in a loop or sleeping with a non-blocking socket. If you use poll(2), you can specify a timeout after which the operation returns 0 if no file descriptor was ready, so you can already control a timeout without needing to resort to sleep.
Same advise as bk2204 outlined: Just use poll. If you want to have a separate thread, a simple way to signal that thread is to add the read side of a pipe (or socket) to the set of polled file descriptors. The main thread then closes the write side when it wants the thread to stop. poll will then return and signal that reading from the pipe is possible (since it will signal EOF).
Here is the outline of an implementation:
We start by defining an RAII class for file descriptors.
#include <unistd.h>
// using pipe, close
#include <utility>
// using std::swap, std::exchange
struct FileHandle
{
int fd;
constexpr FileHandle(int fd=-1) noexcept
: fd(fd)
{}
FileHandle(FileHandle&& o) noexcept
: fd(std::exchange(o.fd, -1))
{}
~FileHandle()
{
if(fd >= 0)
::close(fd);
}
void swap(FileHandle& o) noexcept
{
using std::swap;
swap(fd, o.fd);
}
FileHandle& operator=(FileHandle&& o) noexcept
{
FileHandle tmp = std::move(o);
swap(tmp);
return *this;
}
operator bool() const noexcept
{ return fd >= 0; }
void reset(int fd=-1) noexcept
{ *this = FileHandle(fd); }
void close() noexcept
{ reset(); }
};
Then we use that to construct our pipe or socket pair.
#include <cerrno>
#include <system_error>
struct Pipe
{
FileHandle receive, send;
Pipe()
{
int fds[2];
if(pipe(fds))
throw std::system_error(errno, std::generic_category(), "pipe");
receive.reset(fds[0]);
send.reset(fds[1]);
}
};
The thread then uses poll on the receive end and its signalfd.
#include <poll.h>
#include <signal.h>
#include <sys/signalfd.h>
#include <cassert>
void processOSSignals(const FileHandle& stop)
{
sigset_t mask;
sigemptyset(&mask);
FileHandle sighandle{ signalfd(-1, &mask, 0) };
if(! sighandle)
throw std::system_error(errno, std::generic_category(), "signalfd");
struct pollfd fds[2];
fds[0].fd = sighandle.fd;
fds[1].fd = stop.fd;
fds[0].events = fds[1].events = POLLIN;
while(true) {
if(poll(fds, 2, -1) < 0)
throw std::system_error(errno, std::generic_category(), "poll");
if(fds[1].revents & POLLIN) // stop signalled
break;
struct signalfd_siginfo fdsi;
// will not block
assert(fds[0].revents != 0);
auto readedBytes = read(sighandle.fd, &fdsi, sizeof(fdsi));
}
}
All that remains to be done is create our various RAII classes in such an order that the write side of the pipe is closed before the thread is joined.
#include <thread>
int main()
{
std::thread ossThread;
Pipe stop; // declare after thread so it is destroyed first
ossThread = std::thread(processOSSignals, std::move(stop.receive));
programLoop();
stop.send.close(); // also handled by destructor
ossThread.join();
}
Other things to note:
Consider switching to std::jthread so that it joins automatically even if the program loop throws an exception
Depending on what your background thread does, you can also simply abandon it on program end by calling std::thread::detach
If the thread may stay busy (not calling poll) for long loops, you can pair the pipe up with an std::atomic<bool> or jthread's std::stop_token to signal the stop event. That way the thread can check the flag in between loop iterations. Incidentally, your use of a plain global int was invalid as you read and write from different threads at the same time
You could also use the signalfd and send a specific signal to the thread for it to quit
Related
I have a single-threaded Linux application using boost::asio for asynchronous input/output. Now I need to extend this application to read in GPIO inputs on /sys/class/gpio/gpioXX/value.
It is possible to do that with boost::asio::posix::stream_descriptor on edge-triggered GPIO inputs?
I configured the GPIO input like follows:
echo XX >/sys/class/gpio/export
echo in >/sys/class/gpio/gpioXX/direction
echo both >/sys/class/gpio/gpioXX/edge
I managed to write a epoll based test application that blocks on the GPIO file descriptor until the GPIO signal changes but boost::asio does not seem to be able to block properly. A call to boost::asio::async_read always immediately invokes the handler (of course only within io_service.run()) with either EOF or - in case the file pointer was set back - 2 bytes data.
I'm not an expert in boost::asio internals but could the reason be that the boost::asio epoll reactor is level triggered instead of edge triggered in case of posix::stream_descriptor?
Here is my code:
#include <fcntl.h>
#include <algorithm>
#include <iterator>
#include <stdexcept>
#include <boost/asio.hpp>
boost::asio::io_service io_service;
boost::asio::posix::stream_descriptor sd(io_service);
boost::asio::streambuf streambuf;
void read_handler(const boost::system::error_code& error, std::size_t bytes_transferred)
{
if (error.value() == boost::asio::error::eof) {
// If we don't reset the file pointer we only get EOFs
lseek(sd.native_handle(), 0, SEEK_SET);
} else if (error)
throw std::runtime_error(std::string("Error ") + std::to_string(error.value()) + " occurred (" + error.message() + ")");
std::copy_n(std::istreambuf_iterator<char>(&streambuf), bytes_transferred, std::ostreambuf_iterator<char>(std::cout));
streambuf.consume(bytes_transferred);
boost::asio::async_read(sd, streambuf, &read_handler);
}
int main(int argc, char *argv[])
{
if (argc != 2)
return 1;
int fd = open(argv[1], O_RDONLY);
if (fd < 1)
return 1;
try {
sd.assign(fd);
boost::asio::async_read(sd, streambuf, &read_handler);
io_service.run();
} catch (...) {
close(fd);
return 1;
}
close(fd);
return 0;
}
As far as I know, it is not possible to get this particular behavior with Boost.Asio. While the kernel flags some files on the procfs and sysfs as pollable, they do not provide the stream-like behavior that is expected from boost::asio::posix::stream_descriptor and its operations.
Boost.Asio's epoll reactor is edge-triggered (see Boost.Asio 1.43 revision history notes). Under certain conditions1, Boost.Asio will attempt the I/O operation within the context of the initiating function (e.g. async_read()). If the I/O operation completes (success or failure), then the completion handler is posted into the io_service as-if by io_service.post(). Otherwise, the file descriptor will be added to the event demultiplexer for monitoring. The documentation alludes to this behavior:
Regardless of whether the asynchronous operation completes immediately or not, the handler will not be invoked from within this function. Invocation of the handler will be performed in a manner equivalent to using boost::asio::io_service::post().
For composed operations, such as async_read(), EOF is treated as an error, as it indicates a violation in the operation's contract (i.e. completion condition will never be satisfied because no more data will be available). In this particular case, the I/O system call will occur within the async_read() initiating function, reading from the start of the file (offset 0) to the end of file, causing the operation to fail with boost::asio::error::eof. As the operation has completed, it is never added to the event demultiplexer for edge-triggered monitoring:
boost::asio::io_service io_service;
boost::asio::posix::stream_descriptor stream_descriptor(io_service);
void read_handler(const boost::system::error_code& error, ...)
{
if (error.value() == boost::asio::error::eof)
{
// Reset to start of file.
lseek(sd.native_handle(), 0, SEEK_SET);
}
// Same as below. ::readv() will occur within this context, reading
// from the start of file to end-of-file, causing the operation to
// complete with failure.
boost::asio::async_read(stream_descriptor, ..., &read_handler);
}
int main()
{
int fd = open( /* sysfs file */, O_RDONLY);
// This would throw an exception for normal files, as they are not
// poll-able. However, the kernel flags some files on procfs and
// sysfs as pollable.
stream_descriptor.assign(fd);
// The underlying ::readv() system call will occur within the
// following function (not deferred until edge-triggered notification
// by the reactor). The operation will read from start of file to
// end-of-file, causing the operation to complete with failure.
boost::asio::async_read(stream_descriptor, ..., &read_handler);
// Run will invoke the ready-to-run completion handler from the above
// operation.
io_service.run();
}
1. Internally, Boost.Asio refers to this behavior as speculative operations. It is an implementation detail, but the I/O operation will be attempted within the initiating function if the operation may not need event notification (e.g. it can immediately attempt to a non-blocking I/O call), and and there are neither pending operations of the same type nor pending out-of-band operations on the I/O object. There are no customization hooks to prevent this behavior.
How can a read Linux system call be unblocked in C++? If I have for example in a thread the following loop
:
bool shouldRun;
void foo(){
while(shouldRun){
length = read( file_descriptor, buffer, buffer_length);
//do something
}
return;
}
main(){
shouldRun = true;
std::thread myThread(foo);
//do some other stuff
shouldRun = false;
//-->here I want to unblock "read" in foo
}
Generally the read method should block, I only want to unblock it when needed.
call
fcntl(fd, F_SETFL, flags | O_NONBLOCK);
This will make the file descriptor non-blocking.
the libc read() function internally invoke syscall in kernel side.
The (linux) kernel region is generally not support abort due to its design (yes this can make process non-killable in some case)
To archive your goal, you should make read() non-blocking so that the kernel call return immediately, or check if data ready before read() using select().
I'm going to assume that the read call is waiting for data to become available and it's blocking because of this.
That's why I'd suggest you check if data is available before reading:
#include <sys/ioctl.h>
...
int count;
ioctl(file_descriptor, FIONREAD, &count);
I'm using the function poll() (I think it might be part of POSIX?) C function in my C++ class in order to get an event when a file changes. This seems to work just fine - but now I also want to be able to cause the function to exit immediately when I need to close the thread.
I researched this and came up with a couple of ideas that I tried - like trying to send a signal, but I couldn't figure out how to get this to work.
In the code below (which isn't 100% complete, but should have enough to illustrate the problem), I have a C++ class that starts a thread from the constructor and wants to clean up that thread in the destructor. The thread calls poll() which returns when the file changes, and then it informs the delegate object. The monitoring thread loops until the FileMonitor object indicates it can quit (using a method that returns a bool).
In the destructor, what I would like to do is flip the bool, then do something that causes poll() to exit immediately, and then call *pthread_join()*. So, any ideas on how I can make poll() exit immediately?
This code is targeted towards Linux (specifically debian), but I'm also working on it on a Mac. Ideally it the poll() API should work basically the same.
void * manage_fm(void *arg)
{
FileMonitor * theFileMonitor = (FileMonitor*)arg;
FileMonitorDelegate * delegate;
unsigned char c;
int fd = open(theFileMonitor->filepath2monitor(), O_RDWR);
int count;
ioctl(fd, FIONREAD, &count);
for (int i=0;i<count;++i) {
read(fd, &c, 1);
}
struct pollfd poller;
poller.fd = fd;
poller.events = POLLPRI;
while (theFileMonitor->continue_managing_thread()) {
delegate = theFileMonitor->delegate;
if (poll(&poller, 1, -1) > 0) {
(void) read(fd, &c, 1);
if (delegate) {
delegate->fileChanged();
}
}
}
}
FileMonitor::FileMonitor( )
{
pthread_mutex_init(&mon_mutex, NULL);
manage_thread = true;
pthread_mutex_lock (&mon_mutex);
pthread_create(&thread_id, NULL, manage_fm, this);
pthread_mutex_unlock(&pin_mutex);
}
FileMonitor::~FileMonitor()
{
manage_thread = false;
// I would like to do something here to force the "poll" function to return immediately.
pthread_join(thread_id, NULL);
}
bool FileMonitor::continue_managing_thread()
{
return manage_thread;
}
const char * FileMonitor::filepath2monitor()
{
return "/some/example/file";
}
Add a pipe to your file monitor class and switch your poll to take both your original file descriptor and the pipe's read descriptor to poll on. When you want to wake up your file monitor class for it to check for exit, send a byte through the pipe's write descriptor, that will wake up your thread.
If you have a large number of these file monitors, there's the possibility you could hit the maximum number of file descriptors for a process (See Check the open FD limit for a given process in Linux for details, on my system it's 1024 soft, 4096 hard). You could have multiple monitor classes share a single pipe if you don't mind them all waking up at once to check their exit indicator.
You should use a pthread condition variable inside (and just before) the poll-ing loop, and have the other thread calling pthread_cond_signal
You might consider the pipe(7) to self trick (e.g. have one thread write(2) a byte -perhaps just before pthread_cond_signal- to a pipe poll(2)-ed by another thread who would read(2) the same pipe). See also signal-safety(7) and calling Qt functions from Unix signal handlers. Both could inspire you.
With that pipe-to-self trick, assuming you do poll for reading that pipe, the poll will return. Of course some other thread would have done a write on the same pipe before.
See also Philippe Chaintreuil's answer, he suggests a similar idea.
I normally use single thread process with signal handlers and to achieve concurrency, by dividing parallel tasks into multiple process.
Now, i am trying to check if multi-threading can be faster. To implement alarms/timers, i typically register alarmHandlers and let OS send a signal. But in multi-threading environment, i cannot take this approach, UNLESS, there is a way such that signal can be delivered to a specific thread.
Hence my question, how to implement timers in multithreading environment? I can start a thread and let it sleep for desired amount and then set a shared variable. What other options do i have?
I assume you want to start threads at different times.
You can use the sleep_until function.
This is a C++11 function
The thread will sleep until a certain moment
http://en.cppreference.com/w/cpp/thread/sleep_until
So if you have several tasks to do your code would look like that:
int PerformTask(const std::chrono::time_point<Clock,Duration>& sleep_time, TaskParameters)
{
sleep_until(sleep_time);
PerformTask(TaskParameters);
}
Hope that helps,
You don't specify which environment (OS, API, etc) you are using so any answers you get are going to have to be fairly general.
From your example about starting a thread and having it sleep for a while and then set a shared variable, it sounds like what you're trying to do is have multiple threads all do something special at a particular time, correct?
If so, one easy way to do it would be to choose the alarm-time before spawning the threads, so that each thread can know in advance when to do the special action. Then its just a matter of coding each thread to "watch the clock" and do the action at the appointed time.
But let's say that you don't know in advance when the alarm is supposed to go off. In that case, what I think you need is a mechanism of inter-thread communication. Once you have a way for one thread to send a signal/message to another thread, you can use that to tell the target thread(s) when it's time for them to do the alarm-action.
There are various APIs to do that, but the way I like to use (because it's cross-platform portable and uses the standard BSD sockets API) is to create an entirely-local socket connection before spawning each thread. Under Unix/Posix, you can do this quite easily by calling socketpair(). Under Windows there isn't a socketpair() function to call but you can roll your own socketpair() via the usual networking calls (socket(),bind(),listen(),accept() for one socket, then socket() and connect() to create the other socket and connect it to the first end).
Once you have the pair of connected sockets, you have your parent thread keep only the first socket, and the newly-spawned thread keeps only the second socket. Et voila, now your parent thread and child thread can communicate with each other over the socket. E.g. if your parent thread wants the child thread to do something, it can send() a byte on its socket and the child thread will recv() that byte on its socket, or vice versa if the child thread wants to tell the parent to do something.
In that way, the parent thread could spawn a bunch of threads and then send a byte on each thread's socket when the alarm time arrived. The child threads in the meantime could be doing work and polling their socket via non-blocking recv() calls, or if they prefer to sleep while waiting for the alarm, they could block inside select() or recv() or whatever.
Note that you don't have to send all of your cross-thread data over the socketpair if you don't want to; usually I just lock a mutex, add a command object to a FIFO queue, unlock the mutex, and then send a single byte. When the child thread receives that byte, it responds by locking the same mutex, popping the command object off of the FIFO queue, unlocking the mutex, and then executing the command. That way you can used shared memory to "send" arbitrarily large amounts of data to the child thread without having to send lots of bytes across the socket. The one byte that is sent acts as only a "signal" to wake up the child thread.
Implement a timer with boost::asio
Here is a timer class witch we used in our project, witch project deal with 4Gbit/s internet flow(about 3.0-4.0 million timers). The timer is suit for most generaly work.
timer.h
/*
* Timer
* Licensed under Apache
*
* Author: KaiWen <wenkai1987#gmail.com>
* Date: Apr-16-2013
*
*/
#ifndef TIMER_H
#define TIMER_H
#include <boost/asio.hpp>
#include <boost/thread.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/function.hpp>
#include <boost/unordered_map.hpp>
typedef boost::asio::deadline_timer* timer_ptr;
namespace bs = boost::system;
class timer;
class timer_node;
class tm_callback {
public:
explicit tm_callback(boost::function<void(timer_node&)>& f) : m_f(f)
{
}
void operator()(timer_node& node, const bs::error_code& e) {
if (!e)
m_f(node);
}
private:
boost::function<void(timer_node&)> m_f;
};
class timer_node {
friend class timer;
public:
timer_node() {}
timer_node(timer_ptr p, int ms, boost::function<void(timer_node&)> f) :
m_tptr(p), m_ms(ms), m_callback(f)
{
}
void reset(unsigned int ms = 0, boost::function<void(timer_node&)> f = 0) {
if (ms)
m_tptr->expires_from_now(boost::posix_time::milliseconds(ms));
else
m_tptr->expires_from_now(boost::posix_time::milliseconds(m_ms));
if (f)
m_tptr->async_wait(boost::bind<void>(tm_callback(f), *this, _1));
else
m_tptr->async_wait(boost::bind<void>(tm_callback(m_callback), *this, _1));
}
private:
timer_ptr m_tptr;
int m_ms;
boost::function<void(timer_node&)> m_callback;
};
timer.cpp
/*
* Timer
*
* Licensed under Apache
*
* Author: KaiWen <wenkai1987#gmail.com>
* Date: Apr-16-2013
*
*/
#include "timer.h"
#include <boost/bind.hpp>
#include <boost/date_time/posix_time/ptime.hpp>
namespace ba = boost::asio;
timer::timer(int thread_num) : m_next_ios(0), m_size(0) {
for (int i = 0; i < thread_num; i++) {
io_service_ptr p(new ba::io_service);
work_ptr pw(new ba::io_service::work(*p));
m_ios_list.push_back(p);
m_works.push_back(pw);
}
pthread_spin_init(&m_lock, 0);
}
timer::~timer() {
pthread_spin_destroy(&m_lock);
}
void timer::run() {
for (size_t i = 0; i < m_ios_list.size(); i++)
m_threads.create_thread(boost::bind(&ba::io_service::run, &*m_ios_list[i]))->detach();
}
If you like, combine the timer.cpp to the timer.h, then there is just a header file. A simple usage:
#include <stdio.h>
#include "timer.h"
timer t(3);
void callback(timer_node& nd) {
std::cout << "time out" << std::endl;
t.del_timer(nd);
}
int main(void) {
t.run();
t.add_timer(5000, callback); // set timeout 5 seconds
sleep(6);
return 0;
}
Implement a thread special timer
There is a lock in the timer above, witch cause the program not very fast. You can implement your owen thread special timer, witch not use lock, and not block, fater than the timer above , but this need a 'driver' and implement hardly. Here is a way we implement it:
pkt = get_pkt();
if (pkt) {
now = pkt->sec;
timer.execut_timer(now);
}
Now, here is no lock, and non-block and boost your performance, we use it to deal with 10GBit/s internet flow(about 8.0-9.0 million timers). But this is implement dependence. Hope help you.
recently I set out to port ucos-ii to Ubuntu PC.
As we know, it's not possible to simulate the "process" in the ucos-ii by simply adding a flag in "while" loop in the pthread's call-back function to perform pause and resume(like the solution below). Because the "process" in ucos-ii can be paused or resumed at any time!
How to sleep or pause a PThread in c on Linux
I have found one solution on the web-site below, but it can't be built because it's out of date. It uses the process in Linux to simulate the task(acts like the process in our Linux) in ucos-ii.
http://www2.hs-esslingen.de/~zimmerma/software/index_uk.html
If pthread can act like the process which can be paused and resumed at any time, please tell me some related functions, I can figure it out myself. If it can't, I think I should focus on the older solution. Thanks a lot.
The Modula-3 garbage collector needs to suspend pthreads at an arbitrary time, not just when they are waiting on a condition variable or mutex. It does it by registering a (Unix) signal handler that suspends the thread and then using pthread_kill to send a signal to the target thread. I think it works (it has been reliable for others but I'm debugging an issue with it right now...) It's a bit kludgy, though....
Google for ThreadPThread.m3 and look at the routines "StopWorld" and "StartWorld". Handler itself is in ThreadPThreadC.c.
If stopping at specific points with a condition variable is insufficient, then you can't do this with pthreads. The pthread interface does not include suspend/resume functionality.
See, for example, answer E.4 here:
The POSIX standard provides no mechanism by which a thread A can suspend the execution of another thread B, without cooperation from B. The only way to implement a suspend/restart mechanism is to have B check periodically some global variable for a suspend request and then suspend itself on a condition variable, which another thread can signal later to restart B.
That FAQ answer goes on to describe a couple of non-standard ways of doing it, one in Solaris and one in LinuxThreads (which is now obsolete; do not confuse it with current threading on Linux); neither of those apply to your situation.
On Linux you can probably setup custom signal handler (eg. using signal()) that will contain wait for another signal (eg. using sigsuspend()). You then send the signals using pthread_kill() or tgkill(). It is important to use so-called "realtime signals" for this, because normal signals like SIGUSR1 and SIGUSR2 don't get queued, which means that they can get lost under high load conditions. You send a signal several times, but it gets received only once, because before while signal handler is running, new signals of the same kind are ignored. So if you have concurent threads doing PAUSE/RESUME , you can loose RESUME event and cause deadlock. On the other hand, the pending realtime signals (like SIGRTMIN+1 and SIGRTMIN+2) are not deduplicated, so there can be several same rt signals in queue at the same time.
DISCLAIMER: I had not tried this yet. But in theory it should work.
Also see man 7 signal-safety. There is a list of calls that you can safely call in signal handlers. Fortunately sigsuspend() seems to be one of them.
UPDATE: I have working code right here:
//Filename: pthread_pause.c
//Author: Tomas 'Harvie' Mudrunka 2021
//Build: CFLAGS=-lpthread make pthread_pause; ./pthread_pause
//Test: valgrind --tool=helgrind ./pthread_pause
//I've wrote this code as excercise to solve following stack overflow question:
// https://stackoverflow.com/questions/9397068/how-to-pause-a-pthread-any-time-i-want/68119116#68119116
#define _GNU_SOURCE //pthread_yield() needs this
#include <signal.h>
#include <pthread.h>
//#include <pthread_extra.h>
#include <semaphore.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <unistd.h>
#include <errno.h>
#include <sys/resource.h>
#include <time.h>
#define PTHREAD_XSIG_STOP (SIGRTMIN+0)
#define PTHREAD_XSIG_CONT (SIGRTMIN+1)
#define PTHREAD_XSIGRTMIN (SIGRTMIN+2) //First unused RT signal
pthread_t main_thread;
sem_t pthread_pause_sem;
pthread_once_t pthread_pause_once_ctrl = PTHREAD_ONCE_INIT;
void pthread_pause_once(void) {
sem_init(&pthread_pause_sem, 0, 1);
}
#define pthread_pause_init() (pthread_once(&pthread_pause_once_ctrl, &pthread_pause_once))
#define NSEC_PER_SEC (1000*1000*1000)
// timespec_normalise() from https://github.com/solemnwarning/timespec/
struct timespec timespec_normalise(struct timespec ts)
{
while(ts.tv_nsec >= NSEC_PER_SEC) {
++(ts.tv_sec); ts.tv_nsec -= NSEC_PER_SEC;
}
while(ts.tv_nsec <= -NSEC_PER_SEC) {
--(ts.tv_sec); ts.tv_nsec += NSEC_PER_SEC;
}
if(ts.tv_nsec < 0) { // Negative nanoseconds isn't valid according to POSIX.
--(ts.tv_sec); ts.tv_nsec = (NSEC_PER_SEC + ts.tv_nsec);
}
return ts;
}
void pthread_nanosleep(struct timespec t) {
//Sleep calls on Linux get interrupted by signals, causing premature wake
//Pthread (un)pause is built using signals
//Therefore we need self-restarting sleep implementation
//IO timeouts are restarted by SA_RESTART, but sleeps do need explicit restart
//We also need to sleep using absolute time, because relative time is paused
//You should use this in any thread that gets (un)paused
struct timespec wake;
clock_gettime(CLOCK_MONOTONIC, &wake);
t = timespec_normalise(t);
wake.tv_sec += t.tv_sec;
wake.tv_nsec += t.tv_nsec;
wake = timespec_normalise(wake);
while(clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &wake, NULL)) if(errno!=EINTR) break;
return;
}
void pthread_nsleep(time_t s, long ns) {
struct timespec t;
t.tv_sec = s;
t.tv_nsec = ns;
pthread_nanosleep(t);
}
void pthread_sleep(time_t s) {
pthread_nsleep(s, 0);
}
void pthread_pause_yield() {
//Call this to give other threads chance to run
//Wait until last (un)pause action gets finished
sem_wait(&pthread_pause_sem);
sem_post(&pthread_pause_sem);
//usleep(0);
//nanosleep(&((const struct timespec){.tv_sec=0,.tv_nsec=1}), NULL);
//pthread_nsleep(0,1); //pthread_yield() is not enough, so we use sleep
pthread_yield();
}
void pthread_pause_handler(int signal) {
//Do nothing when there are more signals pending (to cleanup the queue)
//This is no longer needed, since we use semaphore to limit pending signals
/*
sigset_t pending;
sigpending(&pending);
if(sigismember(&pending, PTHREAD_XSIG_STOP)) return;
if(sigismember(&pending, PTHREAD_XSIG_CONT)) return;
*/
//Post semaphore to confirm that signal is handled
sem_post(&pthread_pause_sem);
//Suspend if needed
if(signal == PTHREAD_XSIG_STOP) {
sigset_t sigset;
sigfillset(&sigset);
sigdelset(&sigset, PTHREAD_XSIG_STOP);
sigdelset(&sigset, PTHREAD_XSIG_CONT);
sigsuspend(&sigset); //Wait for next signal
} else return;
}
void pthread_pause_enable() {
//Having signal queue too deep might not be necessary
//It can be limited using RLIMIT_SIGPENDING
//You can get runtime SigQ stats using following command:
//grep -i sig /proc/$(pgrep binary)/status
//This is no longer needed, since we use semaphores
//struct rlimit sigq = {.rlim_cur = 32, .rlim_max=32};
//setrlimit(RLIMIT_SIGPENDING, &sigq);
pthread_pause_init();
//Prepare sigset
sigset_t sigset;
sigemptyset(&sigset);
sigaddset(&sigset, PTHREAD_XSIG_STOP);
sigaddset(&sigset, PTHREAD_XSIG_CONT);
//Register signal handlers
//signal(PTHREAD_XSIG_STOP, pthread_pause_handler);
//signal(PTHREAD_XSIG_CONT, pthread_pause_handler);
//We now use sigaction() instead of signal(), because it supports SA_RESTART
const struct sigaction pause_sa = {
.sa_handler = pthread_pause_handler,
.sa_mask = sigset,
.sa_flags = SA_RESTART,
.sa_restorer = NULL
};
sigaction(PTHREAD_XSIG_STOP, &pause_sa, NULL);
sigaction(PTHREAD_XSIG_CONT, &pause_sa, NULL);
//UnBlock signals
pthread_sigmask(SIG_UNBLOCK, &sigset, NULL);
}
void pthread_pause_disable() {
//This is important for when you want to do some signal unsafe stuff
//Eg.: locking mutex, calling printf() which has internal mutex, etc...
//After unlocking mutex, you can enable pause again.
pthread_pause_init();
//Make sure all signals are dispatched before we block them
sem_wait(&pthread_pause_sem);
//Block signals
sigset_t sigset;
sigemptyset(&sigset);
sigaddset(&sigset, PTHREAD_XSIG_STOP);
sigaddset(&sigset, PTHREAD_XSIG_CONT);
pthread_sigmask(SIG_BLOCK, &sigset, NULL);
sem_post(&pthread_pause_sem);
}
int pthread_pause(pthread_t thread) {
sem_wait(&pthread_pause_sem);
//If signal queue is full, we keep retrying
while(pthread_kill(thread, PTHREAD_XSIG_STOP) == EAGAIN) usleep(1000);
pthread_pause_yield();
return 0;
}
int pthread_unpause(pthread_t thread) {
sem_wait(&pthread_pause_sem);
//If signal queue is full, we keep retrying
while(pthread_kill(thread, PTHREAD_XSIG_CONT) == EAGAIN) usleep(1000);
pthread_pause_yield();
return 0;
}
void *thread_test() {
//Whole process dies if you kill thread immediately before it is pausable
//pthread_pause_enable();
while(1) {
//Printf() is not async signal safe (because it holds internal mutex),
//you should call it only with pause disabled!
//Will throw helgrind warnings anyway, not sure why...
//See: man 7 signal-safety
pthread_pause_disable();
printf("Running!\n");
pthread_pause_enable();
//Pausing main thread should not cause deadlock
//We pause main thread here just to test it is OK
pthread_pause(main_thread);
//pthread_nsleep(0, 1000*1000);
pthread_unpause(main_thread);
//Wait for a while
//pthread_nsleep(0, 1000*1000*100);
pthread_unpause(main_thread);
}
}
int main() {
pthread_t t;
main_thread = pthread_self();
pthread_pause_enable(); //Will get inherited by all threads from now on
//you need to call pthread_pause_enable (or disable) before creating threads,
//otherwise first (un)pause signal will kill whole process
pthread_create(&t, NULL, thread_test, NULL);
while(1) {
pthread_pause(t);
printf("PAUSED\n");
pthread_sleep(3);
printf("UNPAUSED\n");
pthread_unpause(t);
pthread_sleep(1);
/*
pthread_pause_disable();
printf("RUNNING!\n");
pthread_pause_enable();
*/
pthread_pause(t);
pthread_unpause(t);
}
pthread_join(t, NULL);
printf("DIEDED!\n");
}
I am also working on library called "pthread_extra", which will have stuff like this and much more. Will publish soon.
UPDATE2: This is still causing deadlocks when calling pause/unpause rapidly (removed sleep() calls). Printf() implementation in glibc has mutex, so if you suspend thread which is in middle of printf() and then want to printf() from your thread which plans to unpause that thread later, it will never happen, because printf() is locked. Unfortunately i've removed the printf() and only run empty while loop in the thread, but i still get deadlocks under high pause/unpause rates. and i don't know why. Maybe (even realtime) Linux signals are not 100% safe. There is realtime signal queue, maybe it just overflows or something...
UPDATE3: i think i've managed to fix the deadlock, but had to completely rewrite most of the code. Now i have one (sig_atomic_t) variable per each thread which holds state whether that thread should be running or not. Works kinda like condition variable. pthread_(un)pause() transparently remembers this for each thread. I don't have two signals. now i only have one signal. handler of that signal looks at that variable and only blocks on sigsuspend() when that variable says the thread should NOT run. otherwise it returns from signal handler. in order to suspend/resume the thread i now set the sig_atomic_t variable to desired state and call that signal (which is common for both suspend and resume). It is important to use realtime signals to be sure handler will actualy run after you've modified the state variable. Code is bit complex because of the thread status database. I will share the code in separate solution as soon as i manage to simplify it enough. But i want to preserve the two signal version in here, because it kinda works, i like the simplicity and maybe people will give us more insight on how to optimize it.
UPDATE4: I've fixed the deadlock in original code (no need for helper variable holding the status) by using single handler for two signals and optimizing signal queue a bit. There is still some problem with printf() shown by helgrind, but it is not caused by my signals, it happens even when i do not call pause/unpause at all. Overall this was only tested on LINUX, not sure how portable the code is, because there seem to be some undocumented behaviour of signal handlers which was originaly causing the deadlock.
Please note that pause/unpause cannot be nested. if you pause 3 times, and unpause 1 time, the thread WILL RUN. If you need such behaviour, you should create some kind of wrapper which will count the nesting levels and signal the thread accordingly.
UPDATE5: I've improved robustness of the code by following changes: I ensure proper serialization of pause/unpause calls by use of semaphores. This hopefuly fixes last remaining deadlocks. Now you can be sure that when pause call returns, the target thread is actualy already paused. This also solves issues with signal queue overflowing. Also i've added SA_RESTART flag, which prevents internal signals from causing interuption of IO waits. Sleeps/delays still have to be restarted manualy, but i provide convenient wrapper called pthread_nanosleep() which does just that.
UPDATE6: i realized that simply restarting nanosleep() is not enough, because that way timeout does not run when thread is paused. Therefore i've modified pthread_nanosleep() to convert timeout interval to absolute time point in the future and sleep until that. Also i've hidden semaphore initialization, so user does not need to do that.
Here is example of thread function within a class with pause/resume functionality...
class SomeClass
{
public:
// ... construction/destruction
void Resume();
void Pause();
void Stop();
private:
static void* ThreadFunc(void* pParam);
pthread_t thread;
pthread_mutex_t mutex;
pthread_cond_t cond_var;
int command;
};
SomeClass::SomeClass()
{
pthread_mutex_init(&mutex, NULL);
pthread_cond_init(&cond_var, NULL);
// create thread in suspended state..
command = 0;
pthread_create(&thread, NULL, ThreadFunc, this);
}
SomeClass::~SomeClass()
{
// we should stop the thread and exit ThreadFunc before calling of blocking pthread_join function
// also it prevents the mutex staying locked..
Stop();
pthread_join(thread, NULL);
pthread_cond_destroy(&cond_var);
pthread_mutex_destroy(&mutex);
}
void* SomeClass::ThreadFunc(void* pParam)
{
SomeClass* pThis = (SomeClass*)pParam;
timespec time_ns = {0, 50*1000*1000}; // 50 milliseconds
while(1)
{
pthread_mutex_lock(&pThis->mutex);
if (pThis->command == 2) // command to stop thread..
{
// be sure to unlock mutex before exit..
pthread_mutex_unlock(&pThis->mutex);
return NULL;
}
else if (pThis->command == 0) // command to pause thread..
{
pthread_cond_wait(&pThis->cond_var, &pThis->mutex);
// dont forget to unlock the mutex..
pthread_mutex_unlock(&pThis->mutex);
continue;
}
if (pThis->command == 1) // command to run..
{
// normal runing process..
fprintf(stderr, "*");
}
pthread_mutex_unlock(&pThis->mutex);
// it's important to give main thread few time after unlock 'this'
pthread_yield();
// ... or...
//nanosleep(&time_ns, NULL);
}
pthread_exit(NULL);
}
void SomeClass::Stop()
{
pthread_mutex_lock(&mutex);
command = 2;
pthread_cond_signal(&cond_var);
pthread_mutex_unlock(&mutex);
}
void SomeClass::Pause()
{
pthread_mutex_lock(&mutex);
command = 0;
// in pause command we dont need to signal cond_var because we not in wait state now..
pthread_mutex_unlock(&mutex);
}
void SomeClass::Resume()
{
pthread_mutex_lock(&mutex);
command = 1;
pthread_cond_signal(&cond_var);
pthread_mutex_unlock(&mutex);
}