cppthread 1.1.16
C++ Thread Library
thread.cpp
Go to the documentation of this file.
1// Copyright (c) 2013-2025 Made to Order Software Corp. All Rights Reserved
2//
3// https://snapwebsites.org/project/cppthread
4// contact@m2osw.com
5//
6// This program is free software; you can redistribute it and/or modify
7// it under the terms of the GNU General Public License as published by
8// the Free Software Foundation; either version 2 of the License, or
9// (at your option) any later version.
10//
11// This program is distributed in the hope that it will be useful,
12// but WITHOUT ANY WARRANTY; without even the implied warranty of
13// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14// GNU General Public License for more details.
15//
16// You should have received a copy of the GNU General Public License along
17// with this program; if not, write to the Free Software Foundation, Inc.,
18// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19
27// self
28//
29#include "cppthread/thread.h"
30
31#include "cppthread/exception.h"
32#include "cppthread/guard.h"
33#include "cppthread/log.h"
34#include "cppthread/runner.h"
35
36
37// snapdev
38//
39#include <snapdev/glob_to_list.h>
40
41
42// C++
43//
44#include <fstream>
45
46
47// C
48//
49#include <signal.h>
50#include <sys/auxv.h>
51#include <sys/stat.h>
52#include <sys/syscall.h>
53#include <sys/sysinfo.h>
54#include <unistd.h>
55
56
57// last include
58//
59#include <snapdev/poison.h>
60
61
62
63
64namespace cppthread
65{
66
67
68
69
70
89thread::thread(std::string const & name, runner * runner)
90 : f_name(name)
91 , f_runner(runner)
92{
93 init();
94}
95
96
108thread::thread(std::string const & name, std::shared_ptr<runner> runner)
109 : f_name(name)
110 , f_runner(runner.get())
111{
112 init();
113}
114
115
122{
123 if(f_runner == nullptr)
124 {
125 throw invalid_error("runner missing in thread() constructor.");
126 }
127 if(f_runner->f_thread != nullptr)
128 {
129 throw in_use_error(
130 "this runner ("
131 + f_runner->get_name()
132 + ") is already in use.");
133 }
134
135 int err(pthread_attr_init(&f_thread_attr));
136 if(err != 0)
137 {
138 // LCOV_EXCL_START
139 log << log_level_t::fatal
140 << "the thread attributes could not be initialized, error #"
141 << err
142 << '.'
143 << end;
144 throw invalid_error("pthread_attr_init() failed.");
145 // LCOV_EXCL_STOP
146 }
147 err = pthread_attr_setdetachstate(&f_thread_attr, PTHREAD_CREATE_JOINABLE);
148 if(err != 0)
149 {
150 // LCOV_EXCL_START
151 log << log_level_t::fatal
152 << "the thread detach state could not be initialized to joinable, error #"
153 << err
154 << '.'
155 << end;
156 pthread_attr_destroy(&f_thread_attr);
157 throw invalid_error("pthread_attr_setdetachstate() failed");
158 // LCOV_EXCL_STOP
159 }
160
161 f_runner->f_thread = this;
162}
163
164
176{
177 try
178 {
179 stop();
180 }
181 catch(...)
182 {
183 // stop() may rethrow any user exception which we have to ignore in
184 // a destructor...
185 }
186 f_runner->f_thread = nullptr;
187
188 int const err(pthread_attr_destroy(&f_thread_attr));
189 if(err != 0)
190 {
191 // LCOV_EXCL_START
192 log << log_level_t::error
193 << "the thread attributes could not be destroyed, error #"
194 << err
195 << '.'
196 << end;
197 // LCOV_EXCL_STOP
198 }
199}
200
201
209std::string const & thread::get_name() const
210{
211 return f_name;
212}
213
214
232{
233 return f_runner;
234}
235
236
254{
255 guard lock(f_mutex);
256 return f_running;
257}
258
259
270{
271 guard lock(f_mutex);
272 return f_stopping;
273}
274
275
293void * func_internal_start(void * system_thread)
294{
295 thread * t(reinterpret_cast<thread *>(system_thread));
296 t->internal_thread();
297 return nullptr; // == pthread_exit(nullptr);
298}
299
300
319{
320 try
321 {
322 {
323 guard lock(f_mutex);
324 f_tid = gettid();
325 f_started = true;
326 f_mutex.signal();
327 }
328
329 std::string name(f_runner->get_name());
330 if(!name.empty())
331 {
332 // make sure to limit the name to 15 characters
333 //
334 if(name.length() > 15)
335 {
336 name.resize(15);
337 }
338
339 // the pthread_setname_np() allows for the name to be retrieved
340 // with its counter part:
341 //
342 // pthread_getname_np()
343 //
344 pthread_setname_np(pthread_self(), name.c_str());
345
346 // but to really change the name in the comm file (and therefore
347 // htop, ps, etc.) we further call the set_current_thread_name()
348 // function which directly writes to that file
349 //
351 }
352
353 // if the enter failed, do not continue
354 //
355 if(internal_enter())
356 {
357 if(internal_run())
358 {
359 internal_leave(leave_status_t::LEAVE_STATUS_NORMAL);
360 }
361 else
362 {
363 internal_leave(leave_status_t::LEAVE_STATUS_THREAD_FAILED);
364 }
365 }
366 else
367 {
368 internal_leave(leave_status_t::LEAVE_STATUS_INITIALIZATION_FAILED);
369 }
370
371 // if useful (necessary) it would probably be better to call this
372 // function from here; see function and read the "note" section
373 // for additional info
374 //
375 //tcp_client_server::cleanup_on_thread_exit();
376 }
377 catch(std::exception const & e)
378 {
379 // keep a copy of the exception
380 //
381 f_exception = std::current_exception();
382
384 {
385 log << log_level_t::fatal
386 << "thread internal_thread() got exception: \""
387 << e.what()
388 << "\", exiting thread now."
389 << end;
390 }
391
392 internal_leave(leave_status_t::LEAVE_STATUS_INSTRUMENTATION);
393 }
394 catch(...)
395 {
396 // ... any other exception terminates the whole process ...
397 //
398 log << log_level_t::fatal
399 << "thread internal_thread() got an unknown exception (a.k.a. non-std::exception), exiting process."
400 << end;
401
402 // rethrow, our goal is not to ignore the exception, only to
403 // have a log about it
404 //
405 throw;
406 }
407
408 // marked we are done (outside of the try/catch because if this one
409 // fails, we have a big problem... (i.e. invalid mutex or more unlocks
410 // than locks)
411 {
412 guard lock(f_mutex);
413 f_running = false;
415 f_mutex.signal();
416 }
417}
418
419
435{
436 try
437 {
438 f_runner->enter();
439 return true;
440 }
441 catch(std::exception const & e)
442 {
443 // keep a copy of the exception
444 //
445 f_exception = std::current_exception();
446
448 {
449 log << log_level_t::fatal
450 << "thread internal_enter() got exception: \""
451 << e.what()
452 << "\", exiting thread now."
453 << end;
454 }
455 }
456
457 return false;
458}
459
460
484{
485 try
486 {
487 f_runner->run();
488 return true;
489 }
490 catch(std::exception const & e)
491 {
492 // keep a copy of the exception
493 //
494 f_exception = std::current_exception();
495
497 {
498 log << log_level_t::fatal
499 << "thread internal_run() got exception: \""
500 << e.what()
501 << "\", exiting thread now."
502 << end;
503 }
504 }
505
506 return false;
507}
508
509
529{
530 try
531 {
532 f_runner->leave(status);
533 }
534 catch(std::exception const & e)
535 {
536 // keep the first exception (i.e. internal_enter() and internal_run()
537 // have priority on this one)
538 //
539 bool force_log(true);
540 if(f_exception == std::exception_ptr())
541 {
542 f_exception = std::current_exception();
543 force_log = false;
544 }
545
546 if(f_log_all_exceptions || force_log)
547 {
548 log << log_level_t::fatal
549 << "thread internal_leave() got exception: \""
550 << e.what()
551 << "\", exiting thread now."
552 << end;
553 }
554 }
555}
556
557
576{
577 guard lock(f_mutex);
578
579 if(f_running || f_started)
580 {
581 log << log_level_t::warning
582 << "the thread is already running."
583 << end;
584 return false;
585 }
586
587 if(!f_runner->is_ready())
588 {
589 log << log_level_t::warning
590 << "the thread runner is not ready."
591 << end;
592 return false;
593 }
594
595 f_running = true;
596 f_started = false;
597 f_stopping = false; // make sure it is reset
598 f_exception = std::exception_ptr();
599
600 // by default, block all signals in threads
601 //
602 // Note: we check whether the gettid() is equal to the getpid() which
603 // means only the main thread tweaks the signals; this is
604 // important otherwise we could mess up the mask
605 //
606 libexcept::scoped_signal_mask::pointer_t block_all_signals;
607 if(gettid() == getpid())
608 {
609 block_all_signals = std::make_shared<libexcept::scoped_signal_mask>();
610 }
611
612 int const err(pthread_create(&f_thread_id, &f_thread_attr, &func_internal_start, this));
613 if(err != 0)
614 {
615 // LCOV_EXCL_START
616 f_running = false;
617
618 log << log_level_t::error
619 << "the thread could not be created, error #"
620 << err
621 << '.'
622 << end;
623 return false;
624 // LCOV_EXCL_STOP
625 }
626
627 while(!f_started)
628 {
629 f_mutex.wait();
630 }
631
632 return true;
633}
634
635
656void thread::stop(std::function<void(thread *)> callback)
657{
658 {
659 guard lock(f_mutex);
660
661 if(!f_running && !f_started)
662 {
663 // we return immediately in this case because
664 // there is nothing to join when the thread never
665 // started...
666 //
667 return;
668 }
669
670 // request the child to stop
671 //
672 f_stopping = true;
673 }
674
675 if(callback != nullptr)
676 {
677 callback(this);
678 }
679
680 // wait for the child to be stopped
681 //
682 // we cannot pass any results through the pthread interface so we
683 // pass a nullptr for the result; instead, the user is expected to
684 // add fields to his class and fill in whatever results he wants
685 // there; it is going to work much better that way
686 //
687 pthread_join(f_thread_id, nullptr);
688
689 // at this point the thread has fully exited
690
691 // we are done now
692 //
693 // these flags are likely already the correct value except for
694 // f_stopping which the stop() function manages here
695 //
696 f_running = false;
697 f_started = false;
698 f_stopping = false;
699
700 // if the child died because of a standard exception, rethrow it now
701 // and "lose it" at the same time
702 //
703 if(f_exception != std::exception_ptr())
704 {
705 std::exception_ptr e;
706 e.swap(f_exception);
707 std::rethrow_exception(e);
708 }
709}
710
711
727{
728 guard lock(f_mutex);
729 return f_tid;
730}
731
732
741{
742 return f_mutex;
743}
744
745
775void thread::set_log_all_exceptions(bool log_all_exceptions)
776{
777 f_log_all_exceptions = log_all_exceptions;
778}
779
780
804{
806}
807
808
836std::exception_ptr thread::get_exception() const
837{
838 guard lock(f_mutex);
839 return f_exception;
840}
841
842
862bool thread::kill(int sig)
863{
864 guard lock(f_mutex);
865 if(f_running)
866 {
867 // pthread_kill() returns zero on success, otherwise it returns
868 // an error code which at this point we lose
869 //
870 return pthread_kill(f_thread_id, sig) == 0;
871 }
872
873 return false;
874}
875
876
892void thread::mask_signals(libexcept::sig_list_t list)
893{
894 sigset_t set;
895 sigemptyset(&set);
896 for(auto const & s : list)
897 {
898 sigaddset(&set, s);
899 }
900 pthread_sigmask(SIG_BLOCK, &set, nullptr);
901}
902
903
928{
929 sigset_t set;
930 sigfillset(&set);
931 pthread_sigmask(SIG_BLOCK, &set, nullptr);
932}
933
934
947void thread::unmask_signals(libexcept::sig_list_t list)
948{
949 sigset_t set;
950 sigemptyset(&set);
951 for(auto const & s : list)
952 {
953 sigaddset(&set, s);
954 }
955 pthread_sigmask(SIG_UNBLOCK, &set, nullptr);
956}
957
958
959
960
961
962
963
964
980{
981 return get_nprocs_conf();
982}
983
984
1032{
1033 return get_nprocs();
1034}
1035
1036
1047pid_t gettid()
1048{
1049 return static_cast<pid_t>(syscall(SYS_gettid));
1050}
1051
1052
1130{
1131 // here we cache the result (because this value is so unlikely to
1132 // ever change--but really we should watch for changes?)
1133 //
1134 static pid_t pid_max = 0;
1135
1136 if(pid_max == 0)
1137 {
1138 std::ifstream in;
1139 in.open("/proc/sys/kernel/pid_max", std::ios::in | std::ios::binary);
1140 if(in.is_open())
1141 {
1142 char buf[32];
1143 in.getline(buf, sizeof(buf) - 1);
1144 buf[sizeof(buf) - 1] = '\0';
1145 pid_max = std::stol(buf);
1146 }
1147 }
1148
1149 return pid_max - 1;
1150}
1151
1152
1161int set_current_thread_name(std::string const & name)
1162{
1163 return set_thread_name(gettid(), name);
1164}
1165
1166
1194int set_thread_name(pid_t tid, std::string const & name)
1195{
1196 if(name.empty())
1197 {
1198 throw invalid_error("thread name cannot be empty.");
1199 }
1200 if(name.length() > 15)
1201 {
1202 throw out_of_range(
1203 "thread name is limited to 15 characters, \""
1204 + name
1205 + "\" is too long.");
1206 }
1207
1208 // this is what I had in the process implementation, but it only
1209 // can set the current "process" (thread really) and it doesn't
1210 // actually work (that is, the PR_GET_NAME works, but in ps -ef
1211 // or htop, we do not see the change)
1212 //
1213 //if(name != nullptr
1214 //&& *name != '\0')
1215 //{
1216 // prctl(PR_SET_NAME, name);
1217 //}
1218
1219 std::ofstream comm("/proc/" + std::to_string(tid) + "/comm");
1220 comm << name;
1221
1222 return comm ? 0 : -1;
1223}
1224
1225
1237{
1238 return get_thread_name(gettid());
1239}
1240
1241
1259std::string get_thread_name(pid_t tid)
1260{
1261 std::ifstream comm("/proc/" + std::to_string(tid) + "/comm");
1262 std::string name;
1263 comm >> name;
1264 return name;
1265}
1266
1267
1279{
1280 process_ids_t results;
1281
1282 if(pid == -1)
1283 {
1284 pid = getpid();
1285 }
1286
1287 std::string pattern("/proc/");
1288 pattern += std::to_string(pid);
1289 pattern += "/task/*";
1290
1291 snapdev::glob_to_list<std::vector<std::string>> glob;
1292 if(glob.read_path<
1293 snapdev::glob_to_list_flag_t::GLOB_FLAG_IGNORE_ERRORS
1294 , snapdev::glob_to_list_flag_t::GLOB_FLAG_ONLY_DIRECTORIES>(pattern))
1295 {
1296 for(auto s : glob)
1297 {
1298 pid_t id(0);
1299 std::string::size_type pos(s.rfind('/') + 1);
1300
1301 bool valid(pos < s.length());
1302 for(; pos < s.length(); ++pos)
1303 {
1304 char const c(s[pos]);
1305 if(c >= '0' && c <= '9')
1306 {
1307 id = id * 10 + c - '0';
1308 }
1309 else
1310 {
1311 valid = false;
1312 break;
1313 }
1314 }
1315 if(valid)
1316 {
1317 results.push_back(id);
1318 }
1319 }
1320 }
1321
1322 return results;
1323}
1324
1325
1344bool is_process_running(pid_t pid)
1345{
1346 if(pid == getpid())
1347 {
1348 // funny guy testing whether he himself is running!?
1349 //
1350 return true;
1351 }
1352
1353 std::string proc_path("/proc/");
1354 proc_path += std::to_string(pid);
1355
1356 struct stat st;
1357 return stat(proc_path.c_str(), &st) == 0;
1358}
1359
1360
1372std::string get_boot_id()
1373{
1374 std::ifstream in("/proc/sys/kernel/random/boot_id");
1375 std::string uuid;
1376 if(in)
1377 {
1378 std::getline(in, uuid);
1379 }
1380 return uuid;
1381}
1382
1383
1399std::size_t get_thread_count()
1400{
1401 struct stat task;
1402 if(stat("/proc/self/task", &task) != 0)
1403 {
1404 return -1;
1405 }
1406
1407 return task.st_nlink - 2;
1408}
1409
1410
1432{
1433 // the call to getauxval() is only required once; the result can't change
1434 // within one process run (however, it has to be different on each run
1435 // to make it secure)
1436 //
1437 static auto g_vdso(getauxval(AT_SYSINFO_EHDR));
1438 return g_vdso != 0;
1439}
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1702} // namespace cppthread
1703// vim: ts=4 sw=4 et
Lock a mutex in an RAII manner.
Definition guard.h:42
A mutex object to ensures atomicity.
Definition mutex.h:55
void wait()
Wait on a mutex condition.
Definition mutex.cpp:494
void signal()
Signal at least one mutex.
Definition mutex.cpp:737
The runner is the class that wraps the actual system thread.
Definition runner.h:65
virtual void enter()
Signal that the run() function is about to be entered.
Definition runner.cpp:183
virtual void run()=0
This virtual function represents the code run by the thread.
std::string const & get_name() const
Retrieve the name of the runner.
Definition runner.cpp:106
virtual void leave(leave_status_t status)
Signal that the run() function has returned.
Definition runner.cpp:231
thread * f_thread
A pointer back to the owner ("parent") of this runner.
Definition runner.h:98
virtual bool is_ready() const
Check whether this thread runner is ready.
Definition runner.cpp:120
A thread object that ensures proper usage of system threads.
Definition thread.h:64
void mask_all_signals()
Block all signals for this thread.
Definition thread.cpp:927
bool f_stopping
The thread is currently in the stopping process.
Definition thread.h:107
void mask_signals(libexcept::sig_list_t list)
Masks signals from this thread.
Definition thread.cpp:892
pid_t f_tid
This thread identifier.
Definition thread.h:109
std::exception_ptr f_exception
An exception pointer.
Definition thread.h:112
pthread_attr_t f_thread_attr
This thread attributes.
Definition thread.h:111
void set_log_all_exceptions(bool log_all_exceptions)
Whether to log exceptions caught in the thread.
Definition thread.cpp:775
void init()
This private function initializes the thread.
Definition thread.cpp:121
runner * get_runner() const
Get a pointer to this thread runner.
Definition thread.cpp:231
std::string const f_name
The name of this thread.
Definition thread.h:102
bool is_running() const
Check whether the thread is considered to be running.
Definition thread.cpp:253
bool internal_run()
Execute the run() function.
Definition thread.cpp:483
bool get_log_all_exceptions() const
Retrieve whether all exceptions get logged or not.
Definition thread.cpp:803
friend void * func_internal_start(void *system_thread)
Start the actual thread.
Definition thread.cpp:293
void internal_thread()
Run the thread process.
Definition thread.cpp:318
mutex f_mutex
The thread mutex to guard various functions.
Definition thread.h:104
runner * f_runner
The actual thread object.
Definition thread.h:103
void stop(std::function< void(thread *)> callback=nullptr)
Stop the thread.
Definition thread.cpp:656
bool is_stopping() const
Check whether the thread was asked to stop.
Definition thread.cpp:269
std::exception_ptr get_exception() const
Get the exception pointer.
Definition thread.cpp:836
pthread_t f_thread_id
This thread identifier.
Definition thread.h:110
~thread()
Delete a thread object.
Definition thread.cpp:175
void unmask_signals(libexcept::sig_list_t list)
Unmask signals for this thread.
Definition thread.cpp:947
bool f_started
The thread is started.
Definition thread.h:106
std::string const & get_name() const
Retrieve the name of this process object.
Definition thread.cpp:209
bool f_running
The thread is running.
Definition thread.h:105
mutex & get_thread_mutex() const
Retrieve a reference to the thread mutex.
Definition thread.cpp:740
thread(std::string const &name, runner *runner)
Initialize the thread object.
Definition thread.cpp:89
bool internal_enter()
Enter the thread runner.
Definition thread.cpp:434
void internal_leave(leave_status_t status)
Function called when leaving the thread runner.
Definition thread.cpp:528
bool start()
Attempt to start the thread.
Definition thread.cpp:575
pid_t get_thread_tid() const
Retrieve the thread identifier of this thread.
Definition thread.cpp:726
bool f_log_all_exceptions
Whether the runner exceptions should be logged or not.
Definition thread.h:108
bool kill(int sig)
Send a signal to this thread.
Definition thread.cpp:862
Exceptions for the thread environment.
Thread Runner and Managers.
logger log
The logger object used to send logs out.
Definition log.cpp:90
Declaration of the log class used to send error messages.
logger & end(logger &l)
Close a log statement.
Definition log.h:90
Thread Runner and Managers.
leave_status_t
The exit status.
Definition runner.h:53
bool is_process_running(pid_t pid)
Check whether a process is running or not.
Definition thread.cpp:1344
void * func_internal_start(void *system_thread)
Start the actual thread.
Definition thread.cpp:293
pid_t gettid()
Get the thread identifier of the current thread.
Definition thread.cpp:1047
std::size_t get_thread_count()
Get the number of threads still not joined in this process.
Definition thread.cpp:1399
int set_current_thread_name(std::string const &name)
Set the name of the currently running thread.
Definition thread.cpp:1161
int set_thread_name(pid_t tid, std::string const &name)
Set the name of the specified thread.
Definition thread.cpp:1194
process_ids_t get_thread_ids(pid_t pid)
Retrieve the list of threads for a given process.
Definition thread.cpp:1278
bool is_using_vdso()
Certain functions may be implemented using the vDSO library.
Definition thread.cpp:1431
pid_t get_pid_max()
Get the maximum process identifier.
Definition thread.cpp:1129
int get_total_number_of_processors()
Retrieve the number of processors available on this system.
Definition thread.cpp:979
std::string get_boot_id()
Retrieve the boot UUID.
Definition thread.cpp:1372
std::string get_thread_name(pid_t tid)
Retrieve the name of a thread.
Definition thread.cpp:1259
int get_number_of_available_processors()
Retrieve the number of processors currently usable.
Definition thread.cpp:1031
std::string get_current_thread_name()
Retrieve the name of the current thread.
Definition thread.cpp:1236
Thread Runner and Managers.
std::vector< pid_t > process_ids_t
A list of identifiers representing various processes.
Definition thread.h:125
constexpr pid_t PID_UNDEFINED
The value a PID variable is set to when not representing a process.
Definition thread.h:59

This document is part of the Snap! Websites Project.

Copyright by Made to Order Software Corp.