Как один буфер UNIX сигнал таким образом блокирует функции при вызове? В нашем программном обеспечении мы используем сокеты. Теперь мы хотим разблокировать / отменить вызов recv()
через сигнал. Проблема в том, что если сигнал отправляется до ввода recv()
, он теряется и recv()
никогда не разблокируется.
Основная функция выглядит следующим образом:
bool signalHandlerSetup;
bool signalSent;
int main(int argc, char* argv[])
{
pthread_t thread;
signalHandlerSetup = false;
signalSent = false;
// Block the SIGUSR1 signal in the main thread
sigset_t signalSet;
sigemptyset(&signalSet);
sigaddset(&signalSet, SIGUSR1);
pthread_sigmask(SIG_BLOCK, &signalSet, NULL);
// Setup the signal handler for all future threads
struct sigaction signalAction;
signalAction.sa_flags = 0;
signalAction.sa_handler = [](int signalNumber) {};
sigaction(SIGUSR1, &signalAction, NULL);
pthread_create(&thread, NULL, secondaryThreadFunction, NULL);
std::cout << "Started thread" << std::endl;
// Wait until the signal handler is setup
while (!signalHandlerSetup);
pthread_kill(thread, SIGUSR1);
signalSent = true;
pthread_join(thread, NULL);
std::cout << "Joined thread" << std::endl;
}
Вторичный поток просто создает сокет и пытается прочитать из него:
void* secondaryThreadFunction(void *arg)
{
// Setup socket
int internSocket = setupSocket();
// Setup the signal handling
sigset_t signalSet;
sigemptyset(&signalSet);
sigaddset(&signalSet, SIGUSR1);
pthread_sigmask(SIG_UNBLOCK, &signalSet, NULL);
signalHandlerSetup = true;
while (!signalSent);
char buffer;
std::cout << "recv()..." << std::endl;
ssize_t bytesRead = recv(internSocket, static_cast<void*>(&buffer), sizeof(buffer), 0);
std::cout << "recv() canceled" << std::endl;
close(internSocket);
}
Как вы можете видеть Сигнал явно отправляется до ввода функции recv()
, чтобы проиллюстрировать проблему.
Помимо буферизации, для этого могут быть и другие решения, но у каждого из них есть недостатки:
- Подождите, пока вторичный поток заблокирован (Заставляет основной поток блокировать тоже или занято l oop)
- Постоянная отправка сигнала (Другое занято l oop)
РЕДАКТИРОВАТЬ: Сокет не должен быть закрыт только для разблокировки вторичного потока. Мы можем захотеть снова работать на том же сокете после разблокировки.
РЕДАКТИРОВАТЬ 2: Я смог решить проблему с помощью решения, предоставленного Мартином Джеймсом и Дарреном Смитом. Сейчас я использую функцию select()
в сочетании с дескриптором файла событий. Для справки вот рабочее решение:
int eventDescriptor;
int main(int argc, char* argv[])
{
pthread_t thread;
// Create the event file descriptor
eventDescriptor = eventfd(
0, // Initial value
0 // Flags
);
if (eventDescriptor == -1)
{
std::cout << "Failed to create event file descriptor" << std::endl;
return -1;
}
pthread_create(&thread, NULL, secondaryThreadFunction, NULL);
std::cout << "Started thread" << std::endl;
// Notify the event descriptor
uint64_t valueToWrite = 1;
if (write(eventDescriptor, &valueToWrite, sizeof(uint64_t)) == -1)
{
std::cout << "Failed to write to event file descriptor" << std::endl;
return -1;
}
pthread_join(thread, NULL);
std::cout << "Joined thread" << std::endl;
close(eventDescriptor);
return 0;
}
Вот вторичный поток:
void* secondaryThreadFunction(void *arg)
{
// Setup socket
int internSocket = setupSocket();
// Set up the file descriptor set
fd_set readFileDescriptorSet;
FD_ZERO(&readFileDescriptorSet);
FD_SET(internSocket, &readFileDescriptorSet);
FD_SET(eventDescriptor, &readFileDescriptorSet);
char buffer;
std::cout << "select()..." << std::endl;
int fileDescriptorsSet = select(std::max(internSocket, eventDescriptor) + 1, &readFileDescriptorSet, NULL, NULL, NULL);
if (FD_ISSET(eventDescriptor, &readFileDescriptorSet))
{
std::cout << "select() canceled via event" << std::endl;
}
else if (FD_ISSET(internSocket, &readFileDescriptorSet))
{
std::cout << "select() canceled through socket" << std::endl;
ssize_t bytesRead = recv(internSocket, static_cast<void*>(&buffer), sizeof(buffer), 0);
}
close(internSocket);
}