Consistency in namespace-closing comments
[folly.git] / folly / ssl / detail / OpenSSLThreading.cpp
1 /*
2  * Copyright 2004-present Facebook, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include <folly/ssl/detail/OpenSSLThreading.h>
18
19 #include <mutex>
20
21 #include <folly/Portability.h>
22 #include <folly/SharedMutex.h>
23 #include <folly/SpinLock.h>
24
25 #include <glog/logging.h>
26
27 // We cannot directly use portability/openssl because it also depends on us.
28 // Therefore we directly use openssl includes. Order of includes is important
29 // here. See portability/openssl.h.
30 #include <folly/portability/Windows.h>
31 #include <openssl/crypto.h>
32
33 #if !defined(OPENSSL_IS_BORINGSSL)
34 #define FOLLY_SSL_DETAIL_OPENSSL_IS_110 (OPENSSL_VERSION_NUMBER >= 0x10100000L)
35 #else
36 #define FOLLY_SSL_DETAIL_OPENSSL_IS_110 (false)
37 #endif
38
39 // OpenSSL requires us to provide the implementation of CRYPTO_dynlock_value
40 // so it must be done in the global namespace.
41 struct CRYPTO_dynlock_value {
42   std::mutex mutex;
43 };
44
45 namespace folly {
46 namespace ssl {
47 namespace detail {
48
49 static std::map<int, LockType>& lockTypes() {
50   static auto lockTypesInst = new std::map<int, LockType>();
51   return *lockTypesInst;
52 }
53
54 void setLockTypes(std::map<int, LockType> inLockTypes) {
55 #if FOLLY_SSL_DETAIL_OPENSSL_IS_110
56   LOG(INFO) << "setLockTypes() is unsupported on OpenSSL >= 1.1.0. "
57             << "OpenSSL now uses platform native mutexes";
58 #endif
59
60   lockTypes() = inLockTypes;
61 }
62
63 bool isSSLLockDisabled(int lockId) {
64   const auto& sslLocks = lockTypes();
65   const auto it = sslLocks.find(lockId);
66   return it != sslLocks.end() && it->second == LockType::NONE;
67 }
68
69 namespace {
70 struct SSLLock {
71   explicit SSLLock(LockType inLockType = LockType::MUTEX)
72       : lockType(inLockType) {}
73
74   void lock(bool read) {
75     if (lockType == LockType::MUTEX) {
76       mutex.lock();
77     } else if (lockType == LockType::SPINLOCK) {
78       spinLock.lock();
79     } else if (lockType == LockType::SHAREDMUTEX) {
80       if (read) {
81         sharedMutex.lock_shared();
82       } else {
83         sharedMutex.lock();
84       }
85     }
86     // lockType == LOCK_NONE, no-op
87   }
88
89   void unlock(bool read) {
90     if (lockType == LockType::MUTEX) {
91       mutex.unlock();
92     } else if (lockType == LockType::SPINLOCK) {
93       spinLock.unlock();
94     } else if (lockType == LockType::SHAREDMUTEX) {
95       if (read) {
96         sharedMutex.unlock_shared();
97       } else {
98         sharedMutex.unlock();
99       }
100     }
101     // lockType == LOCK_NONE, no-op
102   }
103
104   LockType lockType;
105   folly::SpinLock spinLock{};
106   std::mutex mutex;
107   SharedMutex sharedMutex;
108 };
109 } // namespace
110
111 // Statics are unsafe in environments that call exit().
112 // If one thread calls exit() while another thread is
113 // references a member of SSLContext, bad things can happen.
114 // SSLContext runs in such environments.
115 // Instead of declaring a static member we "new" the static
116 // member so that it won't be destructed on exit().
117 static std::unique_ptr<SSLLock[]>& locks() {
118   static auto locksInst = new std::unique_ptr<SSLLock[]>();
119   return *locksInst;
120 }
121
122 static void callbackLocking(int mode, int n, const char*, int) {
123   if (mode & CRYPTO_LOCK) {
124     locks()[size_t(n)].lock(mode & CRYPTO_READ);
125   } else {
126     locks()[size_t(n)].unlock(mode & CRYPTO_READ);
127   }
128 }
129
130 static unsigned long callbackThreadID() {
131   return static_cast<unsigned long>(folly::getCurrentThreadID());
132 }
133
134 static CRYPTO_dynlock_value* dyn_create(const char*, int) {
135   return new CRYPTO_dynlock_value;
136 }
137
138 static void
139 dyn_lock(int mode, struct CRYPTO_dynlock_value* lock, const char*, int) {
140   if (lock != nullptr) {
141     if (mode & CRYPTO_LOCK) {
142       lock->mutex.lock();
143     } else {
144       lock->mutex.unlock();
145     }
146   }
147 }
148
149 static void dyn_destroy(struct CRYPTO_dynlock_value* lock, const char*, int) {
150   delete lock;
151 }
152
153 void installThreadingLocks() {
154   // static locking
155   locks().reset(new SSLLock[size_t(CRYPTO_num_locks())]);
156   for (auto it : lockTypes()) {
157     locks()[size_t(it.first)].lockType = it.second;
158   }
159   CRYPTO_set_id_callback(callbackThreadID);
160   CRYPTO_set_locking_callback(callbackLocking);
161   // dynamic locking
162   CRYPTO_set_dynlock_create_callback(dyn_create);
163   CRYPTO_set_dynlock_lock_callback(dyn_lock);
164   CRYPTO_set_dynlock_destroy_callback(dyn_destroy);
165 }
166
167 void cleanupThreadingLocks() {
168   CRYPTO_set_id_callback(nullptr);
169   CRYPTO_set_locking_callback(nullptr);
170   CRYPTO_set_dynlock_create_callback(nullptr);
171   CRYPTO_set_dynlock_lock_callback(nullptr);
172   CRYPTO_set_dynlock_destroy_callback(nullptr);
173   locks().reset();
174 }
175
176 } // namespace detail
177 } // namespace ssl
178 } // namespace folly