diff --git a/Makefile b/Makefile index 399c861..f9ec1f6 100644 --- a/Makefile +++ b/Makefile @@ -36,6 +36,12 @@ override CFLAGS += -DWITH_GPIO endif +WITH_PTHREAD_NP ?= 1 +ifneq ($(call optbool,$(WITH_PTHREAD_NP)),) +override CFLAGS += -DWITH_PTHREAD_NP +endif + + # ===== all: $(SOURCES) $(PROG) diff --git a/README.md b/README.md index 8ef6b6f..c5d575d 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ If you're going to live-stream from your backyard webcam and need to control it, # Building You'll need ```make```, ```gcc```, ```libevent``` with ```pthreads``` support, ```libjpeg8```/```libjpeg-turbo``` and ```libuuid```. -On Raspberry Pi you can build the program with OpenMAX IL. To do this pass option ```WITH_OMX=1``` to ```make```. To enable GPIO support install [wiringPi](http://wiringpi.com) and pass option ```WITH_GPIO=1```. +On Raspberry Pi you can build the program with OpenMAX IL. To do this pass option ```WITH_OMX=1``` to ```make```. To enable GPIO support install [wiringPi](http://wiringpi.com) and pass option ```WITH_GPIO=1```. If the compiler reports about a missing function ```pthread_get_name_np()``` (or similar), add option ```WITH_PTHREAD_NP=0``` (it's enabled by default). ``` $ git clone --depth=1 https://github.com/pikvm/ustreamer diff --git a/README.ru.md b/README.ru.md index a58faac..d55ab6d 100644 --- a/README.ru.md +++ b/README.ru.md @@ -32,7 +32,7 @@ # Сборка Для сборки вам понадобятся ```make```, ```gcc```, ```libevent``` с поддержкой ```pthreads```, ```libjpeg8```/```libjpeg-turbo``` и ```libuuid```. -На Raspberry Pi программу можно собрать с поддержкой OpenMAX IL. Для этого передайте ```make``` параметр ```WITH_OMX=1```. Для включения сборки с поддержкой GPIO установите [wiringPi](http://wiringpi.com) и добавьте параметр ```WITH_GPIO=1```. +На Raspberry Pi программу можно собрать с поддержкой OpenMAX IL. Для этого передайте ```make``` параметр ```WITH_OMX=1```. Для включения сборки с поддержкой GPIO установите [wiringPi](http://wiringpi.com) и добавьте параметр ```WITH_GPIO=1```. Если при сборке компилятор ругается на отсутствие функции ```pthread_get_name_np()``` или другой подобной, добавьте параметр ```WITH_PTHREAD_NP=0``` (по умолчанию он включен). ``` $ git clone --depth=1 https://github.com/pikvm/ustreamer diff --git a/src/encoder.c b/src/encoder.c index 0a57200..995b657 100644 --- a/src/encoder.c +++ b/src/encoder.c @@ -29,6 +29,7 @@ #include #include "tools.h" +#include "threading.h" #include "logging.h" #include "device.h" diff --git a/src/encoder.h b/src/encoder.h index 456849c..831f2df 100644 --- a/src/encoder.h +++ b/src/encoder.h @@ -24,7 +24,7 @@ #include -#include "pthread.h" +#include #include "tools.h" #include "device.h" diff --git a/src/http/server.c b/src/http/server.c index d62b260..d6e0e02 100644 --- a/src/http/server.c +++ b/src/http/server.c @@ -50,6 +50,7 @@ #endif #include "../tools.h" +#include "../threading.h" #include "../logging.h" #include "../picture.h" #include "../encoder.h" diff --git a/src/logging.h b/src/logging.h index f368bb1..81c2e7e 100644 --- a/src/logging.h +++ b/src/logging.h @@ -34,6 +34,7 @@ #include #include "tools.h" +#include "threading.h" enum { @@ -51,13 +52,13 @@ pthread_mutex_t log_mutex; #define LOGGING_INIT { \ log_level = LOG_LEVEL_INFO; \ log_colored = isatty(1); \ - assert(!pthread_mutex_init(&log_mutex, NULL)); \ + A_MUTEX_INIT(&log_mutex); \ } -#define LOGGING_DESTROY assert(!pthread_mutex_destroy(&log_mutex)) +#define LOGGING_DESTROY A_MUTEX_DESTROY(&log_mutex) -#define LOGGING_LOCK assert(!pthread_mutex_lock(&log_mutex)) -#define LOGGING_UNLOCK assert(!pthread_mutex_unlock(&log_mutex)) +#define LOGGING_LOCK A_MUTEX_LOCK(&log_mutex) +#define LOGGING_UNLOCK A_MUTEX_UNLOCK(&log_mutex) #define COLOR_GRAY "\x1b[30;1m" @@ -87,12 +88,14 @@ pthread_mutex_t log_mutex; #define LOG_PRINTF_NOLOCK(_label_color, _label, _msg_color, _msg, ...) { \ + char _buf[MAX_THREAD_NAME] = {0}; \ + thread_get_name(_buf); \ if (log_colored) { \ - printf(COLOR_GRAY "-- " _label_color _label COLOR_GRAY " [%.03Lf tid=%d]" " -- " COLOR_RESET _msg_color _msg COLOR_RESET, \ - get_now_monotonic(), get_thread_id(), ##__VA_ARGS__); \ + printf(COLOR_GRAY "-- " _label_color _label COLOR_GRAY " [%.03Lf %9s]" " -- " COLOR_RESET _msg_color _msg COLOR_RESET, \ + get_now_monotonic(), _buf, ##__VA_ARGS__); \ } else { \ - printf("-- " _label " [%.03Lf tid=%d] -- " _msg, \ - get_now_monotonic(), get_thread_id(), ##__VA_ARGS__); \ + printf("-- " _label " [%.03Lf %9s] -- " _msg, \ + get_now_monotonic(), _buf, ##__VA_ARGS__); \ } \ putchar('\n'); \ fflush(stdout); \ @@ -109,7 +112,7 @@ pthread_mutex_t log_mutex; } #define LOG_PERROR(_msg, ...) { \ - char _buf[1024] = ""; \ + char _buf[1024] = {0}; \ char *_ptr = errno_to_string(_buf, 1024); \ LOG_ERROR(_msg ": %s", ##__VA_ARGS__, _ptr); \ } diff --git a/src/main.c b/src/main.c index 9f1ab19..fa3f688 100644 --- a/src/main.c +++ b/src/main.c @@ -33,6 +33,7 @@ #include #include "tools.h" +#include "threading.h" #include "logging.h" #include "options.h" #include "device.h" @@ -60,12 +61,14 @@ static void _block_thread_signals(void) { } static void *_stream_loop_thread(UNUSED void *arg) { + A_THREAD_RENAME("stream"); _block_thread_signals(); stream_loop(_ctx->stream); return NULL; } static void *_server_loop_thread(UNUSED void *arg) { + A_THREAD_RENAME("http"); _block_thread_signals(); http_server_loop(_ctx->server); return NULL; @@ -105,6 +108,8 @@ int main(int argc, char *argv[]) { LOGGING_INIT; + A_THREAD_RENAME("main"); + # ifdef WITH_GPIO GPIO_INIT; # endif @@ -150,6 +155,9 @@ int main(int argc, char *argv[]) { GPIO_SET_LOW(prog_running); # endif + if (exit_code == 0) { + LOG_INFO("Bye-bye"); + } LOGGING_DESTROY; return (exit_code < 0 ? 1 : 0); } diff --git a/src/stream.c b/src/stream.c index 7e2ce3c..61cc216 100644 --- a/src/stream.c +++ b/src/stream.c @@ -34,6 +34,7 @@ #include #include "tools.h" +#include "threading.h" #include "logging.h" #include "xioctl.h" #include "picture.h" @@ -424,6 +425,7 @@ static void _workers_pool_destroy(struct _workers_pool_t *pool) { static void *_worker_thread(void *v_worker) { struct _worker_t *worker = (struct _worker_t *)v_worker; + A_THREAD_RENAME("worker-%u", worker->number); LOG_DEBUG("Hello! I am a worker #%u ^_^", worker->number); # ifdef WITH_GPIO diff --git a/src/threading.h b/src/threading.h new file mode 100644 index 0000000..c850729 --- /dev/null +++ b/src/threading.h @@ -0,0 +1,109 @@ +/***************************************************************************** +# # +# uStreamer - Lightweight and fast MJPG-HTTP streamer. # +# # +# Copyright (C) 2018 Maxim Devaev # +# # +# This program is free software: you can redistribute it and/or modify # +# it under the terms of the GNU General Public License as published by # +# the Free Software Foundation, either version 3 of the License, or # +# (at your option) any later version. # +# # +# This program is distributed in the hope that it will be useful, # +# but WITHOUT ANY WARRANTY; without even the implied warranty of # +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # +# GNU General Public License for more details. # +# # +# You should have received a copy of the GNU General Public License # +# along with this program. If not, see . # +# # +*****************************************************************************/ + + +#pragma once + +#include +#include +#include + +#include +#include + +#include +#ifdef WITH_PTHREAD_NP +# if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) +# include +# include +# endif +#endif + +#include "tools.h" + + +#ifdef PTHREAD_MAX_NAMELEN_NP +# define MAX_THREAD_NAME PTHREAD_MAX_NAMELEN_NP +#else +# define MAX_THREAD_NAME 16 +#endif + +#define A_THREAD_CREATE(_tid, _func, _arg) assert(!pthread_create(_tid, NULL, _func, _arg)) +#define A_THREAD_JOIN(_tid) assert(!pthread_join(_tid, NULL)) + +#ifdef WITH_PTHREAD_NP +# define A_THREAD_RENAME(_fmt, ...) { \ + char _buf[MAX_THREAD_NAME] = {0}; \ + assert(snprintf(_buf, MAX_THREAD_NAME, _fmt, ##__VA_ARGS__) > 0); \ + thread_set_name(_buf); \ + } +#else +# define A_THREAD_RENAME(_fmt, ...) +#endif + +#define A_MUTEX_INIT(_mutex) assert(!pthread_mutex_init(_mutex, NULL)) +#define A_MUTEX_DESTROY(_mutex) assert(!pthread_mutex_destroy(_mutex)) +#define A_MUTEX_LOCK(_mutex) assert(!pthread_mutex_lock(_mutex)) +#define A_MUTEX_UNLOCK(_mutex) assert(!pthread_mutex_unlock(_mutex)) + +#define A_COND_INIT(_cond) assert(!pthread_cond_init(_cond, NULL)) +#define A_COND_DESTROY(_cond) assert(!pthread_cond_destroy(_cond)) +#define A_COND_SIGNAL(...) assert(!pthread_cond_signal(__VA_ARGS__)) +#define A_COND_WAIT_TRUE(_var, _cond, _mutex) { while(!_var) assert(!pthread_cond_wait(_cond, _mutex)); } + + +#ifdef WITH_PTHREAD_NP +INLINE void thread_set_name(const char *name) { +# if defined(__linux__) + pthread_setname_np(pthread_self(), name); +# elif defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) + pthread_set_name_np(pthread_self(), name); +# elif defined(__NetBSD__) + pthread_setname_np(pthread_self(), "%s", (void *)name); +# else +# error thread_set_name() not implemented, you can disable it using WITH_PTHREAD_NP=0 +# endif +} +#endif + +INLINE void thread_get_name(char *name) { // Always required for logging +#ifdef WITH_PTHREAD_NP + int retval = -1; +# if defined(__linux__) || defined (__NetBSD__) + retval = pthread_getname_np(pthread_self(), name, MAX_THREAD_NAME); +# elif \ + (defined(__FreeBSD__) && defined(__FreeBSD_version) && __FreeBSD_version >= 1103500) \ + || (defined(__OpenBSD__) && defined(OpenBSD) && OpenBSD >= 201905) \ + || defined(__DragonFly__) + pthread_get_name_np(pthread_self(), name, MAX_THREAD_NAME); + if (name[0] != '\0') { + retval = 0; + } +# else +# error thread_get_name() not implemented, you can disable it using WITH_PTHREAD_NP=0 +# endif + if (retval < 0) { +#endif + assert(snprintf(name, MAX_THREAD_NAME, "tid=%d", (pid_t)syscall(SYS_gettid)) > 0); +#ifdef WITH_PTHREAD_NP + } +#endif +} diff --git a/src/tools.h b/src/tools.h index 248032c..80e64c5 100644 --- a/src/tools.h +++ b/src/tools.h @@ -25,30 +25,11 @@ #include #include #include -#include #include #include -#include #include #include -#include -#include -#include - - -#define A_THREAD_CREATE(_tid, _func, _arg) assert(!pthread_create(_tid, NULL, _func, _arg)) -#define A_THREAD_JOIN(_tid) assert(!pthread_join(_tid, NULL)) - -#define A_MUTEX_INIT(_mutex) assert(!pthread_mutex_init(_mutex, NULL)) -#define A_MUTEX_DESTROY(_mutex) assert(!pthread_mutex_destroy(_mutex)) -#define A_MUTEX_LOCK(_mutex) assert(!pthread_mutex_lock(_mutex)) -#define A_MUTEX_UNLOCK(_mutex) assert(!pthread_mutex_unlock(_mutex)) - -#define A_COND_INIT(_cond) assert(!pthread_cond_init(_cond, NULL)) -#define A_COND_DESTROY(_cond) assert(!pthread_cond_destroy(_cond)) -#define A_COND_SIGNAL(...) assert(!pthread_cond_signal(__VA_ARGS__)) -#define A_COND_WAIT_TRUE(_var, _cond, _mutex) { while(!_var) assert(!pthread_cond_wait(_cond, _mutex)); } #define A_CALLOC(_dest, _nmemb) assert((_dest = calloc(_nmemb, sizeof(*(_dest))))) #define A_REALLOC(_dest, _nmemb) assert((_dest = realloc(_dest, _nmemb * sizeof(*(_dest))))) @@ -104,7 +85,3 @@ INLINE long double get_now_real(void) { get_now(CLOCK_REALTIME, &sec, &msec); return (long double)sec + ((long double)msec) / 1000; } - -INLINE pid_t get_thread_id(void) { - return syscall(SYS_gettid); -}