OpenVPN 3 Core Library
Loading...
Searching...
No Matches
pthreadcond.hpp
Go to the documentation of this file.
1// OpenVPN -- An application to securely tunnel IP networks
2// over a single port, with support for SSL/TLS-based
3// session authentication and key exchange,
4// packet encryption, packet authentication, and
5// packet compression.
6//
7// Copyright (C) 2012- OpenVPN Inc.
8//
9// SPDX-License-Identifier: MPL-2.0 OR AGPL-3.0-only WITH openvpn3-openssl-exception
10//
11
12#ifndef OPENVPN_COMMON_PTHREADCOND_H
13#define OPENVPN_COMMON_PTHREADCOND_H
14
15#include <mutex>
16#include <condition_variable>
17#include <chrono>
18
20
21namespace openvpn {
22
23// Barrier class that is useful in cases where all threads
24// need to reach a known point before executing some action.
25// Note that this barrier implementation is
26// constructed using C++11 condition variables.
28{
29 enum State
30 {
31 UNSIGNALED = 0, // initial state
32 SIGNALED, // signal() was called
33 ERROR_THROWN, // error() was called
34 };
35
36 public:
37 // status return from wait()
38 enum Status
39 {
40 SUCCESS = 0, // successful
41 CHOSEN_ONE, // successful and chosen (only one thread is chosen)
42 TIMEOUT, // timeout
43 ERROR_SIGNAL, // at least one thread called error()
44 };
45
46 PThreadBarrier(const int initial_limit = -1)
47 : stop(nullptr),
48 limit(initial_limit)
49 {
50 }
51
52 PThreadBarrier(Stop *stop_arg, const int initial_limit = -1)
53 : stop(stop_arg),
54 limit(initial_limit)
55 {
56 }
57
58 // All callers will increment count and block until
59 // count == limit. CHOSEN_ONE will be returned to
60 // the first caller to reach limit. This caller can
61 // then release all the other callers by calling
62 // signal().
63 int wait(const unsigned int seconds)
64 {
65 // allow asynchronous stop
66 Stop::Scope stop_scope(stop, [this]()
67 { error(); });
68
69 bool timeout = false;
70 int ret;
71 std::unique_lock<std::mutex> lock(mutex);
72 const unsigned int c = ++count;
73 while (state == UNSIGNALED
74 && (limit < 0 || c < static_cast<unsigned int>(limit))
75 && !timeout)
76 timeout = (cv.wait_for(lock, std::chrono::seconds(seconds)) == std::cv_status::timeout);
77 if (timeout)
78 ret = TIMEOUT;
79 else if (state == ERROR_THROWN)
81 else if (state == UNSIGNALED && !chosen)
82 {
84 chosen = true;
85 }
86 else
87 ret = SUCCESS;
88 return ret;
89 }
90
91 void set_limit(const int new_limit)
92 {
93 std::unique_lock<std::mutex> lock(mutex);
94 limit = new_limit;
95 cv.notify_all();
96 }
97
98 // Generally, only the CHOSEN_ONE calls signal() after its work
99 // is complete, to allow the other threads to pass the barrier.
100 void signal()
101 {
103 }
104
105 // Causes all threads waiting on wait() (and those which call wait()
106 // in the future) to exit with ERROR_SIGNAL status.
107 void error()
108 {
110 }
111
112 private:
113 void signal_(const State newstate)
114 {
115 std::unique_lock<std::mutex> lock(mutex);
116 if (state == UNSIGNALED)
117 {
118 state = newstate;
119 cv.notify_all();
120 }
121 }
122
123 std::mutex mutex;
124 std::condition_variable cv;
127 bool chosen = false;
128 int count = 0;
129 int limit;
130};
131
132} // namespace openvpn
133
134#endif
std::condition_variable cv
void signal_(const State newstate)
void set_limit(const int new_limit)
PThreadBarrier(const int initial_limit=-1)
PThreadBarrier(Stop *stop_arg, const int initial_limit=-1)
int wait(const unsigned int seconds)
std::string ret