Last active
January 22, 2024 10:35
-
-
Save bitonic/d3281b2d0fd95b4fd788aa7e013d1fb9 to your computer and use it in GitHub Desktop.
Stopping linux threads example
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// See <https://mazzo.li/posts/stopping-linux-threads.html> | |
// for blog post. | |
// | |
// Spawns a thread with a server listening on 55555 UDP, and | |
// then terminates it after 1 minute. | |
// | |
// I compile and run with | |
// | |
// clang++ -Wall -std=c++20 server.cpp -lpthread -o server && ./server | |
// | |
// Then you can | |
// | |
// echo "hello" | nc -u 127.0.0.1 55555 | |
// | |
// to test. | |
#include <stdio.h> | |
#include <string.h> | |
#include <sys/socket.h> | |
#include <netinet/in.h> | |
#include <unistd.h> | |
#include <signal.h> | |
#include <stdlib.h> | |
#include <pthread.h> | |
#include <errno.h> | |
#include <atomic> | |
#include <vector> | |
static void die_error(const char* what, int err) { | |
fprintf(stderr, "%s: %s\n", what, strerror(err)); | |
exit(1); | |
} | |
static void die_syscall(const char* what) { | |
die_error(what, errno); | |
} | |
// thread_local isn't really necessary here with one thread, | |
// but it would be necessary if we had many threads we wanted | |
// to kill separatedly. | |
static thread_local std::atomic<bool> stop = false; | |
static void stop_thread_handler(int signum) { | |
stop.store(true); | |
} | |
static void* server(void*) { | |
// create socket | |
int sock = socket(AF_INET, SOCK_DGRAM, 0); | |
if (sock < 0) { | |
die_syscall("socket"); | |
} | |
// bind to 55555 | |
struct sockaddr_in servaddr = { 0 }; | |
servaddr.sin_family = AF_INET; | |
servaddr.sin_addr.s_addr = INADDR_ANY; | |
servaddr.sin_port = htons(55555); | |
if (bind(sock, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0) { | |
die_syscall("bind"); | |
} | |
printf("listening on port 55555...\n"); | |
// process | |
std::vector<char> buffer(9000); // jumbo frames anyone? | |
for (;;) { | |
printf("waiting for packet...\n"); | |
ssize_t recvlen; | |
if (stop.load()) { | |
break; | |
} else { | |
recvlen = recvfrom(sock, buffer.data(), buffer.size(), 0, nullptr, nullptr); | |
} | |
if (recvlen < 0 && errno == EINTR && stop.load()) { | |
break; | |
} | |
if (recvlen < 0) { | |
die_syscall("recvfrom"); | |
} | |
printf("got packet.\n"); | |
size_t written = 0; | |
while (written < recvlen) { | |
ssize_t r = write(STDOUT_FILENO, &buffer[written], recvlen-written); | |
if (r < 0) { | |
die_syscall("write"); | |
} | |
written += r; | |
} | |
} | |
// close socket before exiting | |
close(sock); | |
return nullptr; | |
} | |
int main() { | |
// install signal handler | |
{ | |
struct sigaction act = {{ 0 }}; | |
act.sa_handler = &stop_thread_handler; | |
if (sigaction(SIGUSR1, &act, nullptr) < 0) { | |
die_syscall("sigaction"); | |
} | |
} | |
printf("starting server in different thread\n"); | |
pthread_t thr; | |
{ | |
int res = pthread_create(&thr, nullptr, &server, nullptr); | |
if (res != 0) { | |
die_error("pthread_create", res); | |
} | |
} | |
sleep(60); | |
printf("stopping server\n"); | |
{ | |
int res = pthread_kill(thr, SIGUSR1); | |
if (res != 0) { | |
die_error("pthread_kill", res); | |
} | |
res = pthread_join(thr, nullptr); | |
if (res != 0) { | |
die_error("pthread_join", res); | |
} | |
} | |
return 0; | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment