Direct-BT v3.3.0-1-gc2d430c
Direct-BT - Direct Bluetooth Programming.
L2CAPComm.cpp
Go to the documentation of this file.
1/*
2 * Author: Sven Gothel <sgothel@jausoft.com>
3 * Copyright (c) 2020 Gothel Software e.K.
4 * Copyright (c) 2020 ZAFENA AB
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining
7 * a copy of this software and associated documentation files (the
8 * "Software"), to deal in the Software without restriction, including
9 * without limitation the rights to use, copy, modify, merge, publish,
10 * distribute, sublicense, and/or sell copies of the Software, and to
11 * permit persons to whom the Software is furnished to do so, subject to
12 * the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be
15 * included in all copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 */
25
26#include <cstring>
27#include <string>
28#include <memory>
29#include <cstdint>
30#include <vector>
31#include <cstdio>
32
33#include <algorithm>
34
35// #define PERF_PRINT_ON 1
36#include <jau/debug.hpp>
37
38#include "L2CAPComm.hpp"
39#include "DBTConst.hpp"
40
41#include "BTIoctl.hpp"
42#include "L2CAPIoctl.hpp"
43
44#include "BTDevice.hpp"
45
46extern "C" {
47 #include <unistd.h>
48 #include <sys/socket.h>
49 #include <poll.h>
50 #include <pthread.h>
51 #include <signal.h>
52
53 #if defined(__FreeBSD__)
54 // FIXME
55 #define SOL_BLUETOOTH 274
56 #endif
57}
58
59using namespace direct_bt;
60
61L2CAPEnv::L2CAPEnv() noexcept
62: exploding( jau::environment::getExplodingProperties("direct_bt.l2cap") ),
63 L2CAP_READER_POLL_TIMEOUT( jau::environment::getInt32Property("direct_bt.l2cap.reader.timeout", 10000, 1500 /* min */, INT32_MAX /* max */) ),
64 L2CAP_RESTART_COUNT_ON_ERROR( jau::environment::getInt32Property("direct_bt.l2cap.restart.count", 5, INT32_MIN /* min */, INT32_MAX /* max */) ), // FIXME: Move to L2CAPComm
65 DEBUG_DATA( jau::environment::getBooleanProperty("direct_bt.debug.l2cap.data", false) )
66{
67}
68
69// *************************************************
70// *************************************************
71// *************************************************
72
73/**
74 * Setting BT_SECURITY within open() after bind() and before connect()
75 * causes BlueZ/Kernel to immediately process SMP, leading to a potential deadlock.
76 *
77 * Here we experience, setting security level before connect()
78 * will block the thread within connect(), potentially a mutex used in the SMP procedure.
79 *
80 * Hence we set BT_SECURITY after connect() within open().
81 */
82inline constexpr const bool SET_BT_SECURITY_POST_CONNECT = true;
83
84std::string L2CAPComm::getStateString(bool isOpen, bool hasIOError) noexcept {
85 return "State[open "+std::to_string(isOpen)+
86 ", ioerr "+std::to_string(hasIOError)+
87 ", errno "+std::to_string(errno)+" ("+std::string(strerror(errno))+")]";
88}
89
90std::string L2CAPComm::getStateString(bool isOpen, bool irqed_int, bool irqed_ext, bool hasIOError) noexcept {
91 return "State[open "+std::to_string(isOpen)+
92 ", irqed "+std::to_string(irqed_int || irqed_ext)+" [int "+std::to_string(irqed_int)+", ext "+std::to_string(irqed_ext)+
93 "], ioerr "+std::to_string(hasIOError)+
94 ", errno "+std::to_string(errno)+" ("+std::string(strerror(errno))+")]";
95}
96
97int L2CAPComm::l2cap_open_dev(const BDAddressAndType & adapterAddressAndType, const L2CAP_PSM psm, const L2CAP_CID cid) noexcept {
98 sockaddr_l2 a;
99 int fd, err;
100
101#if defined(__linux__)
102 // OK, tested
103#elif defined(__FreeBSD__)
104 // #warning add implementation
105 ABORT("add implementation for FreeBSD");
106#else
107 #warning add implementation
108 ABORT("add implementation");
109#endif
110
111 // Create a loose L2CAP socket
112 fd = ::socket(AF_BLUETOOTH, // AF_BLUETOOTH == PF_BLUETOOTH
113 SOCK_SEQPACKET, BTPROTO_L2CAP);
114
115 if( 0 > fd ) {
116 ERR_PRINT("L2CAPComm::l2cap_open_dev: socket failed");
117 return fd;
118 }
119
120 // Bind socket to the L2CAP adapter
121 // BT Core Spec v5.2: Vol 3, Part A: L2CAP_CONNECTION_REQ
122 bzero((void *)&a, sizeof(a));
123 a.l2_family=AF_BLUETOOTH;
124 a.l2_psm = jau::cpu_to_le(direct_bt::number(psm));
125 a.l2_bdaddr = jau::cpu_to_le(adapterAddressAndType.address);
126 a.l2_cid = jau::cpu_to_le(direct_bt::number(cid));
127 a.l2_bdaddr_type = ::number(adapterAddressAndType.type);
128 if ( ::bind(fd, (struct sockaddr *) &a, sizeof(a)) < 0 ) {
129 ERR_PRINT("L2CAPComm::l2cap_open_dev: bind failed");
130 goto failed;
131 }
132 return fd;
133
134failed:
135 err = errno;
136 ::close(fd);
137 errno = err;
138
139 return -1;
140}
141
142int L2CAPComm::l2cap_close_dev(int dd) noexcept
143{
144 return ::close(dd);
145}
146
147L2CAPComm::L2CAPComm(const uint16_t adev_id_, BDAddressAndType localAddressAndType_, const L2CAP_PSM psm_, const L2CAP_CID cid_) noexcept
148: env(L2CAPEnv::get()),
149 adev_id(adev_id_),
150 localAddressAndType(std::move(localAddressAndType_)),
151 psm(psm_), cid(cid_),
152 socket_(-1),
153 is_open_(false), interrupted_intern(false), is_interrupted_extern(/* Null Type */)
154{ }
155
156bool L2CAPComm::setBTSecurityLevelImpl(const BTSecurityLevel sec_level, const BDAddressAndType& remoteAddressAndType) noexcept {
157 if( BTSecurityLevel::NONE > sec_level ) {
158 DBG_PRINT("L2CAP::setBTSecurityLevel: sec_level %s not set: dev_id %u, dd %d, %s, psm %s, cid %s; %s",
159 to_string(sec_level).c_str(),
160 adev_id, socket_.load(), remoteAddressAndType.toString().c_str(),
161 to_string(psm).c_str(), to_string(cid).c_str(),
162 getStateString().c_str());
163 return false;
164 }
165
166 if constexpr ( USE_LINUX_BT_SECURITY ) {
167 struct bt_security bt_sec;
168 int result;
169
170 BTSecurityLevel old_sec_level = getBTSecurityLevelImpl(remoteAddressAndType);
171 if( old_sec_level != sec_level ) {
172 bzero(&bt_sec, sizeof(bt_sec));
173 bt_sec.level = direct_bt::number(sec_level);
174 result = ::setsockopt(socket_, SOL_BLUETOOTH, BT_SECURITY, &bt_sec, sizeof(bt_sec));
175 if ( 0 == result ) {
176 DBG_PRINT("L2CAP::setBTSecurityLevel: Success: sec_level %s -> %s: dev_id %u, dd %d, %s, psm %s, cid %s; %s",
177 to_string(old_sec_level).c_str(), to_string(sec_level).c_str(),
178 adev_id, socket_.load(), remoteAddressAndType.toString().c_str(),
179 to_string(psm).c_str(), to_string(cid).c_str(),
180 getStateString().c_str());
181 return true;
182 } else {
183 ERR_PRINT("L2CAP::setBTSecurityLevel: Failed: sec_level %s -> %s: dev_id %u, dd %d, %s, psm %s, cid %s; %s",
184 to_string(old_sec_level).c_str(), to_string(sec_level).c_str(),
185 adev_id, socket_.load(), remoteAddressAndType.toString().c_str(),
186 to_string(psm).c_str(), to_string(cid).c_str(),
187 getStateString().c_str());
188 return false;
189 }
190 } else {
191 DBG_PRINT("L2CAP::setBTSecurityLevel: Unchanged: sec_level %s -> %s: dev_id %u, dd %d, %s, psm %s, cid %s; %s",
192 to_string(old_sec_level).c_str(), to_string(sec_level).c_str(),
193 adev_id, socket_.load(), remoteAddressAndType.toString().c_str(),
194 to_string(psm).c_str(), to_string(cid).c_str(),
195 getStateString().c_str());
196 return true;
197 }
198 } else {
199 DBG_PRINT("L2CAP::setBTSecurityLevel: Not implemented: sec_level %s: dev_id %u, dd %d, %s, psm %s, cid %s; %s",
200 to_string(sec_level).c_str(),
201 adev_id, socket_.load(), remoteAddressAndType.toString().c_str(),
202 to_string(psm).c_str(), to_string(cid).c_str(),
203 getStateString().c_str());
204 return false;
205 }
206}
207
210 if constexpr ( USE_LINUX_BT_SECURITY ) {
211 struct bt_security bt_sec;
212 socklen_t optlen = sizeof(bt_sec);
213 int result;
214
215 bzero(&bt_sec, sizeof(bt_sec));
216 result = ::getsockopt(socket_, SOL_BLUETOOTH, BT_SECURITY, &bt_sec, &optlen);
217 if ( 0 == result ) {
218 if( optlen == sizeof(bt_sec) ) {
219 sec_level = static_cast<BTSecurityLevel>(bt_sec.level);
220 DBG_PRINT("L2CAP::getBTSecurityLevel: Success: sec_level %s: dev_id %u, dd %d, %s, psm %s, cid %s; %s",
221 to_string(sec_level).c_str(),
222 adev_id, socket_.load(), remoteAddressAndType.toString().c_str(),
223 to_string(psm).c_str(), to_string(cid).c_str(),
224 getStateString().c_str());
225 } else {
226 ERR_PRINT("L2CAP::getBTSecurityLevel: Failed: sec_level %s, size %zd returned != %zd bt_sec: dev_id %u, dd %d, %s, psm %s, cid %s; %s",
227 to_string(sec_level).c_str(), optlen, sizeof(bt_sec),
228 adev_id, socket_.load(), remoteAddressAndType.toString().c_str(),
229 to_string(psm).c_str(), to_string(cid).c_str(),
230 getStateString().c_str());
231 }
232 } else {
233 ERR_PRINT("L2CAP::getBTSecurityLevel: Failed: sec_level %s, result %d: dev_id %u, dd %d, %s, psm %s, cid %s; %s",
234 to_string(sec_level).c_str(), result,
235 adev_id, socket_.load(), remoteAddressAndType.toString().c_str(),
236 to_string(psm).c_str(), to_string(cid).c_str(),
237 getStateString().c_str());
238 }
239 } else {
240 DBG_PRINT("L2CAP::getBTSecurityLevel: Not implemented: sec_level %s: dev_id %u, dd %d, %s, psm %s, cid %s; %s",
241 to_string(sec_level).c_str(),
242 adev_id, socket_.load(), remoteAddressAndType.toString().c_str(),
243 to_string(psm).c_str(), to_string(cid).c_str(),
244 getStateString().c_str());
245 }
246 return sec_level;
247}
248
249// *************************************************
250// *************************************************
251// *************************************************
252
253L2CAPClient::L2CAPClient(const uint16_t adev_id_, BDAddressAndType adapterAddressAndType_, const L2CAP_PSM psm_, const L2CAP_CID cid_) noexcept
254: L2CAPComm(adev_id_, std::move(adapterAddressAndType_), psm_, cid_),
255 remoteAddressAndType(BDAddressAndType::ANY_BREDR_DEVICE),
256 has_ioerror(false), tid_connect(0), tid_read(0)
257{ }
258
259L2CAPClient::L2CAPClient(const uint16_t adev_id_, BDAddressAndType adapterAddressAndType_, const L2CAP_PSM psm_, const L2CAP_CID cid_,
260 BDAddressAndType remoteAddressAndType_, int client_socket_) noexcept
261: L2CAPComm(adev_id_, std::move(adapterAddressAndType_), psm_, cid_),
262 remoteAddressAndType(std::move(remoteAddressAndType_)),
263 has_ioerror(false), tid_connect(0), tid_read(0)
264{
265 socket_ = client_socket_;
266 is_open_ = 0 <= client_socket_;
267}
268
269bool L2CAPClient::open(const BTDevice& device, const BTSecurityLevel sec_level) noexcept {
270
271 bool expOpen = false; // C++11, exp as value since C++20
272 if( !is_open_.compare_exchange_strong(expOpen, true) ) {
273 DBG_PRINT("L2CAPClient::open(%s, %s): Already open: dev_id %u, dd %d, %s, psm %s, cid %s; %s",
274 device.getAddressAndType().toString().c_str(), to_string(sec_level).c_str(),
275 adev_id, socket_.load(), remoteAddressAndType.toString().c_str(),
276 to_string(psm).c_str(), to_string(cid).c_str(),
277 getStateString().c_str());
278 return false;
279 }
280 const std::lock_guard<std::recursive_mutex> lock(mtx_write); // RAII-style acquire and relinquish via destructor
281
282 has_ioerror = false; // always clear last ioerror flag (should be redundant)
283
284 /**
285 * bt_io_connect ( create_io ) with source address
286 * - fd = socket(.._)
287 * - bind(fd, ..)
288 * - l2cap_set
289 * -- set imtu, omtu, mode
290 * -- l2cap_set_master
291 * -- l2cap_set_flushable
292 * -- set_priority
293 * -- set_sec_level
294 * --- setsockopt(.. BT_SECURITY ..)
295 *
296 * - l2cap_connect with destination address
297 * -- connect(fd, ..)
298 */
299 remoteAddressAndType = device.getAddressAndType();
300
301 /** BT Core Spec v5.2: Vol 3, Part A: L2CAP_CONNECTION_REQ */
302 sockaddr_l2 req;
303 int res;
304 int to_retry_count=0; // ETIMEDOUT retry count
305
306 DBG_PRINT("L2CAPClient::open: Start Connect: dev_id %u, dd %d, %s, psm %s, cid %s, sec_level %s; %s",
307 adev_id, socket_.load(), remoteAddressAndType.toString().c_str(),
308 to_string(psm).c_str(), to_string(cid).c_str(), to_string(sec_level).c_str(),
309 getStateString().c_str());
310
311 socket_ = l2cap_open_dev(localAddressAndType, psm, cid);
312
313 if( 0 > socket_ ) {
314 goto failure; // open failed
315 }
316
318 if( BTSecurityLevel::UNSET < sec_level ) {
319 if( !setBTSecurityLevelImpl(sec_level, remoteAddressAndType) ) {
320 goto failure; // sec_level failed
321 }
322 }
323 }
324
325 tid_connect = ::pthread_self(); // temporary safe tid to allow interruption
326
327 // actual request to connect to remote device
328 bzero((void *)&req, sizeof(req));
329 req.l2_family = AF_BLUETOOTH;
330 req.l2_psm = jau::cpu_to_le(direct_bt::number(psm));
331 req.l2_bdaddr = jau::cpu_to_le(remoteAddressAndType.address);
332 req.l2_cid = jau::cpu_to_le(direct_bt::number(cid));
333 req.l2_bdaddr_type = ::number(remoteAddressAndType.type);
334
335 while( !interrupted() ) {
336 // blocking
337 res = ::connect(socket_, (struct sockaddr*)&req, sizeof(req));
338
339 DBG_PRINT("L2CAPClient::open: Connect Result: %d, errno 0x%X %s, dev_id %u, %s, psm %s, cid %s",
340 res, errno, strerror(errno),
341 adev_id, remoteAddressAndType.toString().c_str(),
342 to_string(psm).c_str(), to_string(cid).c_str());
343
344 if( !res )
345 {
346 break; // done
347
348 } else if( ETIMEDOUT == errno ) {
349 to_retry_count++;
350 if( to_retry_count < number(Defaults::L2CAP_CONNECT_MAX_RETRY) ) {
351 WORDY_PRINT("L2CAPClient::open: Connect timeout, retry %d: dev_id %u, dd %d, %s, psm %s, cid %s, sec_level %s; %s",
352 to_retry_count,
353 adev_id, socket_.load(), remoteAddressAndType.toString().c_str(),
354 to_string(psm).c_str(), to_string(cid).c_str(), to_string(sec_level).c_str(),
355 getStateString().c_str());
356 continue;
357 } else {
358 ERR_PRINT("L2CAPClient::open: Connect timeout, retried %d: dev_id %u, dd %d, %s, psm %s, cid %s, sec_level %s; %s",
359 to_retry_count,
360 adev_id, socket_.load(), remoteAddressAndType.toString().c_str(),
361 to_string(psm).c_str(), to_string(cid).c_str(), to_string(sec_level).c_str(),
362 getStateString().c_str());
363 goto failure; // exit
364 }
365
366 } else if( !interrupted() ) {
367 // EALREADY == errno || ENETUNREACH == errno || EHOSTUNREACH == errno || ..
368 ERR_PRINT("L2CAPClient::open: Connect failed: dev_id %u, dd %d, %s, psm %s, cid %s, sec_level %s; %s",
369 adev_id, socket_.load(), remoteAddressAndType.toString().c_str(),
370 to_string(psm).c_str(), to_string(cid).c_str(), to_string(sec_level).c_str(),
371 getStateString().c_str());
372 goto failure; // exit
373 } else {
374 goto failure; // exit on interrupt
375 }
376 }
377 // success
378 tid_connect = 0;
379
381 if( BTSecurityLevel::UNSET < sec_level ) {
382 if( !setBTSecurityLevelImpl(sec_level, remoteAddressAndType) ) {
383 goto failure; // sec_level failed
384 }
385 }
386 }
387
388 return true;
389
390failure:
391 const int err = errno;
392 close();
393 errno = err;
394 return false;
395}
396
397bool L2CAPClient::close_impl() noexcept {
398 bool expOpen = true; // C++11, exp as value since C++20
399 if( !is_open_.compare_exchange_strong(expOpen, false) ) {
400 DBG_PRINT("L2CAPClient::close: Not connected: dev_id %u, dd %d, %s, psm %s, cid %s; %s",
401 adev_id, socket_.load(), remoteAddressAndType.toString().c_str(),
402 to_string(psm).c_str(), to_string(cid).c_str(),
403 getStateString().c_str());
404 has_ioerror = false; // always clear last ioerror flag (should be redundant)
406 return true;
407 }
408 const std::lock_guard<std::recursive_mutex> lock(mtx_write); // RAII-style acquire and relinquish via destructor
409
410 DBG_PRINT("L2CAPClient::close: Start: dev_id %u, dd %d, %s, psm %s, cid %s; %s",
411 adev_id, socket_.load(), remoteAddressAndType.toString().c_str(),
412 to_string(psm).c_str(), to_string(cid).c_str(),
413 getStateString().c_str());
414 has_ioerror = false;
416 PERF_TS_T0();
417
418 // interrupt connect() and read(), avoiding prolonged hang
419 interrupted_intern = true;
420 {
421 ::pthread_t tid_self = ::pthread_self();
422 ::pthread_t _tid_connect = tid_connect;
423 ::pthread_t _tid_read = tid_read;
424 tid_read = 0;
425 tid_connect = 0;
426
427 // interrupt read(), avoiding prolonged hang
428 if( 0 != _tid_read && tid_self != _tid_read ) {
429 int kerr;
430 if( 0 != ( kerr = ::pthread_kill(_tid_read, SIGALRM) ) ) {
431 ERR_PRINT("L2CAPClient::close: pthread_kill read %p FAILED: %d; dev_id %u, dd %d, %s, psm %s, cid %s; %s",
432 (void*)_tid_read, kerr, // NOLINT(performance-no-int-to-ptr)
433 adev_id, socket_.load(), remoteAddressAndType.toString().c_str(),
434 to_string(psm).c_str(), to_string(cid).c_str(),
435 getStateString().c_str());
436 }
437 }
438 // interrupt connect(), avoiding prolonged hang
439 if( 0 != _tid_connect && _tid_read != _tid_connect && tid_self != _tid_connect ) {
440 int kerr;
441 if( 0 != ( kerr = ::pthread_kill(_tid_connect, SIGALRM) ) ) {
442 ERR_PRINT("L2CAPClient::close: Start: pthread_kill connect %p FAILED: %d; dev_id %u, dd %d, %s, psm %s, cid %s; %s",
443 (void*)_tid_connect, kerr, // NOLINT(performance-no-int-to-ptr)
444 adev_id, socket_.load(), remoteAddressAndType.toString().c_str(),
445 to_string(psm).c_str(), to_string(cid).c_str(),
446 getStateString().c_str());
447 }
448 }
449 }
450
452 socket_ = -1;
453 interrupted_intern = false;
454 PERF_TS_TD("L2CAPClient::close");
455 DBG_PRINT("L2CAPClient::close: End: dev_id %u, dd %d, %s, psm %s, cid %s; %s",
456 adev_id, socket_.load(), remoteAddressAndType.toString().c_str(),
457 to_string(psm).c_str(), to_string(cid).c_str(),
458 getStateString().c_str());
459 return true;
460}
461
462bool L2CAPClient::setBTSecurityLevel(const BTSecurityLevel sec_level) noexcept {
463 if( !is_open_ ) {
464 DBG_PRINT("L2CAPClient::setBTSecurityLevel(%s): Not connected: dev_id %u, dd %d, %s, psm %s, cid %s; %s",
465 to_string(sec_level).c_str(),
466 adev_id, socket_.load(), remoteAddressAndType.toString().c_str(),
467 to_string(psm).c_str(), to_string(cid).c_str(),
468 getStateString().c_str());
469 return false;
470 }
471 const std::lock_guard<std::recursive_mutex> lock(mtx_write); // RAII-style acquire and relinquish via destructor
472 if( BTSecurityLevel::UNSET < sec_level ) {
473 if( setBTSecurityLevelImpl(sec_level, remoteAddressAndType) ) {
474 return true;
475 } else {
476 close();
477 return false;
478 }
479 } else {
480 return true;
481 }
482}
483
485 if( !is_open_ ) {
486 DBG_PRINT("L2CAPClient::getBTSecurityLevel: Not connected: dev_id %u, dd %d, %s, psm %s, cid %s; %s",
487 adev_id, socket_.load(), remoteAddressAndType.toString().c_str(),
488 to_string(psm).c_str(), to_string(cid).c_str(),
489 getStateString().c_str());
491 }
492 const std::lock_guard<std::recursive_mutex> lock(mtx_write); // RAII-style acquire and relinquish via destructor
493
494 return getBTSecurityLevelImpl(remoteAddressAndType);
495}
496
497#define RWEXITCODE_ENUM(X) \
498 X(RWExitCode, SUCCESS) \
499 X(RWExitCode, NOT_OPEN) \
500 X(RWExitCode, INTERRUPTED) \
501 X(RWExitCode, INVALID_SOCKET_DD) \
502 X(RWExitCode, POLL_ERROR) \
503 X(RWExitCode, POLL_TIMEOUT) \
504 X(RWExitCode, READ_ERROR) \
505 X(RWExitCode, READ_TIMEOUT) \
506 X(RWExitCode, WRITE_ERROR)
507
508#define CASE2_TO_STRING(U,V) case U::V: return #V;
509
510std::string L2CAPClient::getRWExitCodeString(const RWExitCode ec) noexcept {
511 if( number(ec) >= 0 ) {
512 return "SUCCESS";
513 }
514 switch(ec) {
516 default: ; // fall through intended
517 }
518 return "Unknown ExitCode";
519}
520
521jau::snsize_t L2CAPClient::read(uint8_t* buffer, const jau::nsize_t capacity) noexcept {
522 const int32_t timeoutMS = env.L2CAP_READER_POLL_TIMEOUT;
523 jau::snsize_t len = 0;
524 jau::snsize_t err_res = 0;
525
526 if( !is_open_ ) {
527 err_res = number(RWExitCode::NOT_OPEN);
528 goto errout;
529 }
530 if( interrupted() ) {
531 err_res = number(RWExitCode::INTERRUPTED);
532 goto errout;
533 }
534 if( 0 > socket_ ) {
535 err_res = number(RWExitCode::INVALID_SOCKET_DD);
536 goto errout;
537 }
538 if( 0 == capacity ) {
539 goto done;
540 }
541
542 tid_read = ::pthread_self(); // temporary safe tid to allow interruption
543
544 if( timeoutMS ) {
545 struct pollfd p;
546 int n = 1;
547
548 p.fd = socket_; p.events = POLLIN;
549 while ( is_open_ && !interrupted() && ( n = ::poll( &p, 1, timeoutMS ) ) < 0 ) {
550 if( !is_open_ ) {
551 err_res = number(RWExitCode::NOT_OPEN);
552 goto errout;
553 }
554 if( interrupted() ) {
555 err_res = number(RWExitCode::INTERRUPTED);
556 goto errout;
557 }
558 if ( errno == EAGAIN || errno == EINTR ) {
559 // cont temp unavail or interruption
560 continue;
561 }
562 if( errno == ETIMEDOUT ) {
563 err_res = number(RWExitCode::POLL_TIMEOUT);
564 } else {
565 err_res = number(RWExitCode::POLL_ERROR);
566 }
567 goto errout;
568 }
569 if ( 0 == n ) {
570 err_res = number(RWExitCode::POLL_TIMEOUT);
571 errno = ETIMEDOUT;
572 goto errout;
573 }
574 }
575
576 while ( is_open_ && !interrupted() && ( len = ::read(socket_, buffer, capacity) ) < 0 ) {
577 if( !is_open_ ) {
578 err_res = number(RWExitCode::NOT_OPEN);
579 goto errout;
580 }
581 if( interrupted() ) {
582 err_res = number(RWExitCode::INTERRUPTED);
583 goto errout;
584 }
585 if ( errno == EAGAIN || errno == EINTR ) {
586 // cont temp unavail or interruption
587 continue;
588 }
589 if( errno == ETIMEDOUT ) {
590 err_res = number(RWExitCode::READ_TIMEOUT);
591 } else {
592 err_res = number(RWExitCode::READ_ERROR);
593 }
594 goto errout;
595 }
596
597done:
598 tid_read = 0;
599 return len;
600
601errout:
602 tid_read = 0;
603 if( err_res == number(RWExitCode::NOT_OPEN) ) {
604 WORDY_PRINT("L2CAPClient::read: Not open res %d (%s), len %d; dev_id %u, dd %d, %s, psm %s, cid %s; %s",
605 err_res, getRWExitCodeString(err_res).c_str(), len,
606 adev_id, socket_.load(), remoteAddressAndType.toString().c_str(),
607 to_string(psm).c_str(), to_string(cid).c_str(),
608 getStateString().c_str());
609 } else if( err_res == number(RWExitCode::INTERRUPTED) ) { // interrupted (internal or external)
610 WORDY_PRINT("L2CAPClient::read: IRQed res %d (%s), len %d; dev_id %u, dd %d, %s, psm %s, cid %s; %s",
611 err_res, getRWExitCodeString(err_res).c_str(), len,
612 adev_id, socket_.load(), remoteAddressAndType.toString().c_str(),
613 to_string(psm).c_str(), to_string(cid).c_str(),
614 getStateString().c_str());
615 } else if( err_res != number(RWExitCode::POLL_TIMEOUT) ) { // expected POLL_TIMEOUT if idle
616 // open and not intentionally interrupted
617 if( err_res == number(RWExitCode::READ_TIMEOUT) ) {
618 DBG_PRINT("L2CAPClient::read: Read Timeout res %d (%s), len %d; dev_id %u, dd %d, %s, psm %s, cid %s; %s",
619 err_res, getRWExitCodeString(err_res).c_str(), len,
620 adev_id, socket_.load(), remoteAddressAndType.toString().c_str(),
621 to_string(psm).c_str(), to_string(cid).c_str(),
622 getStateString().c_str());
623 } else { // actual error case
624 has_ioerror = true;
625 if( env.L2CAP_RESTART_COUNT_ON_ERROR < 0 ) {
626 ABORT("L2CAPClient::read: Error res %d (%s), len %d; dev_id %u, dd %d, %s, psm %s, cid %s; %s",
627 err_res, getRWExitCodeString(err_res).c_str(), len,
628 adev_id, socket_.load(), remoteAddressAndType.toString().c_str(),
629 to_string(psm).c_str(), to_string(cid).c_str(),
630 getStateString().c_str());
631 } else {
632 IRQ_PRINT("L2CAPClient::read: Error res %d (%s), len %d; dev_id %u, dd %d, %s, psm %s, cid %s; %s",
633 err_res, getRWExitCodeString(err_res).c_str(), len,
634 adev_id, socket_.load(), remoteAddressAndType.toString().c_str(),
635 to_string(psm).c_str(), to_string(cid).c_str(),
636 getStateString().c_str());
637 }
638 }
639 }
640 return err_res;
641}
642
643jau::snsize_t L2CAPClient::write(const uint8_t * buffer, const jau::nsize_t length) noexcept {
644 const std::lock_guard<std::recursive_mutex> lock(mtx_write); // RAII-style acquire and relinquish via destructor
645 jau::snsize_t len = 0;
646 jau::snsize_t err_res = 0;
647
648 if( !is_open_ ) {
649 err_res = number(RWExitCode::NOT_OPEN);
650 goto errout;
651 }
652 if( interrupted() ) {
653 err_res = number(RWExitCode::INTERRUPTED);
654 goto errout;
655 }
656 if( 0 > socket_ ) {
657 err_res = number(RWExitCode::INVALID_SOCKET_DD);
658 goto errout;
659 }
660 if( 0 == length ) {
661 goto done;
662 }
663
664 while ( is_open_ && !interrupted() && ( len = ::write(socket_, buffer, length) ) < 0 ) {
665 if( !is_open_ ) {
666 err_res = number(RWExitCode::NOT_OPEN);
667 goto errout;
668 }
669 if( interrupted() ) {
670 err_res = number(RWExitCode::INTERRUPTED);
671 goto errout;
672 }
673 if( EAGAIN == errno || EINTR == errno ) {
674 // cont temp unavail or interruption
675 continue;
676 }
677 err_res = number(RWExitCode::WRITE_ERROR);
678 goto errout;
679 }
680
681done:
682 return len;
683
684errout:
685 if( err_res == number(RWExitCode::NOT_OPEN) || err_res == number(RWExitCode::INTERRUPTED) ) {
686 // closed or intentionally interrupted
687 WORDY_PRINT("L2CAPClient::write: IRQed res %d (%s), len %d; dev_id %u, dd %d, %s, psm %s, cid %s; %s",
688 err_res, getRWExitCodeString(err_res).c_str(), len,
689 adev_id, socket_.load(), remoteAddressAndType.toString().c_str(),
690 to_string(psm).c_str(), to_string(cid).c_str(),
691 getStateString().c_str());
692 } else {
693 // open and not intentionally interrupted
694 has_ioerror = true;
695
696 if( env.L2CAP_RESTART_COUNT_ON_ERROR < 0 ) {
697 ABORT("L2CAPClient::write: Error res %d (%s), len %d; dev_id %u, dd %d, %s, psm %s, cid %s; %s",
698 err_res, getRWExitCodeString(err_res).c_str(), len,
699 adev_id, socket_.load(), remoteAddressAndType.toString().c_str(),
700 to_string(psm).c_str(), to_string(cid).c_str(),
701 getStateString().c_str());
702 } else {
703 IRQ_PRINT("L2CAPClient::write: Error res %d (%s), len %d; dev_id %u, dd %d, %s, psm %s, cid %s; %s",
704 err_res, getRWExitCodeString(err_res).c_str(), len,
705 adev_id, socket_.load(), remoteAddressAndType.toString().c_str(),
706 to_string(psm).c_str(), to_string(cid).c_str(),
707 getStateString().c_str());
708 }
709 }
710 return err_res;
711}
712
713std::string L2CAPClient::toString() const noexcept {
714 return "L2CAPClient[dev_id "+std::to_string(adev_id)+", dd "+std::to_string(socket_)+
715 ", psm "+to_string(psm)+
716 ", cid "+to_string(cid)+
717 ", local "+localAddressAndType.toString()+
718 ", remote "+remoteAddressAndType.toString()+
719 ", "+getStateString()+"]";
720}
721
722// *************************************************
723// *************************************************
724// *************************************************
725
726L2CAPServer::L2CAPServer(const uint16_t adev_id_, BDAddressAndType localAddressAndType_, const L2CAP_PSM psm_, const L2CAP_CID cid_) noexcept
727: L2CAPComm(adev_id_, std::move(localAddressAndType_), psm_, cid_), tid_accept(0)
728{ }
729
730bool L2CAPServer::open() noexcept {
731 bool expOpen = false; // C++11, exp as value since C++20
732 if( !is_open_.compare_exchange_strong(expOpen, true) ) {
733 DBG_PRINT("L2CAPServer::open: Already open: dev_id %u, dd %d, psm %s, cid %s, local %s",
734 adev_id, socket_.load(), to_string(psm).c_str(), to_string(cid).c_str(),
735 localAddressAndType.toString().c_str());
736 return false;
737 }
738 const std::lock_guard<std::recursive_mutex> lock(mtx_open); // RAII-style acquire and relinquish via destructor
739 int res;
740
741 DBG_PRINT("L2CAPServer::open: Start: dev_id %u, dd %d, psm %s, cid %s, local %s",
742 adev_id, socket_.load(), to_string(psm).c_str(), to_string(cid).c_str(),
743 localAddressAndType.toString().c_str());
744
746
747 if( 0 > socket_ ) {
748 goto failure; // open failed
749 }
750
751 res = ::listen(socket_, 10);
752
753 DBG_PRINT("L2CAPServer::open: End: res %d, dev_id %u, dd %d, psm %s, cid %s, local %s",
754 res, adev_id, socket_.load(), to_string(psm).c_str(), to_string(cid).c_str(),
755 localAddressAndType.toString().c_str());
756
757 if( res < 0 ) {
758 goto failure;
759 }
760
761 return true;
762
763failure:
764 ERR_PRINT("L2CAPServer::open: Failed: dev_id %u, dd %d, psm %s, cid %s, local %s",
765 adev_id, socket_.load(), to_string(psm).c_str(), to_string(cid).c_str(),
766 localAddressAndType.toString().c_str());
767 const int err = errno;
768 close();
769 errno = err;
770 return false;
771}
772
773bool L2CAPServer::close_impl() noexcept {
774 bool expOpen = true; // C++11, exp as value since C++20
775 if( !is_open_.compare_exchange_strong(expOpen, false) ) {
776 DBG_PRINT("L2CAPServer::close: Not connected: dev_id %u, dd %d, psm %s, cid %s, local %s",
777 adev_id, socket_.load(), to_string(psm).c_str(), to_string(cid).c_str(),
778 localAddressAndType.toString().c_str());
780 return true;
781 }
782 const std::lock_guard<std::recursive_mutex> lock(mtx_open); // RAII-style acquire and relinquish via destructor
783
784 DBG_PRINT("L2CAPServer::close: Start: dev_id %u, dd %d, psm %s, cid %s, local %s",
785 adev_id, socket_.load(), to_string(psm).c_str(), to_string(cid).c_str(),
786 localAddressAndType.toString().c_str());
788 PERF_TS_T0();
789
790 // interrupt accept(..), avoiding prolonged hang
791 interrupted_intern = true;
792 {
793 ::pthread_t tid_self = ::pthread_self();
794 ::pthread_t _tid_accept = tid_accept;
795 tid_accept = 0;
796
797 if( 0 != _tid_accept && tid_self != _tid_accept ) {
798 int kerr;
799 if( 0 != ( kerr = ::pthread_kill(_tid_accept, SIGALRM) ) ) {
800 ERR_PRINT("L2CAPServer::close: Start: pthread_kill connect %p FAILED: %d; dev_id %u, dd %d, psm %s, cid %s, local %s",
801 (void*)_tid_accept, kerr, // NOLINT(performance-no-int-to-ptr)
802 adev_id, socket_.load(), to_string(psm).c_str(), to_string(cid).c_str(),
803 localAddressAndType.toString().c_str());
804 }
805 }
806 }
807
809 socket_ = -1;
810 interrupted_intern = false;
811 PERF_TS_TD("L2CAPServer::close");
812 DBG_PRINT("L2CAPServer::close: End: dev_id %u, dd %d, psm %s, cid %s, local %s",
813 adev_id, socket_.load(), to_string(psm).c_str(), to_string(cid).c_str(),
814 localAddressAndType.toString().c_str());
815 return true;
816}
817
818std::unique_ptr<L2CAPClient> L2CAPServer::accept() noexcept {
819 sockaddr_l2 peer;
820 int to_retry_count=0; // ETIMEDOUT retry count
821
822 tid_accept = ::pthread_self(); // temporary safe tid to allow interruption
823
824 if( !is_open_ ) {
825 ERR_PRINT("L2CAPServer::accept: Not open: dev_id %u, dd[s %d], errno 0x%X %s, psm %s, cid %s, local %s",
826 adev_id, socket_.load(), errno, strerror(errno),
827 to_string(psm).c_str(),
828 to_string(cid).c_str(),
829 localAddressAndType.toString().c_str());
830 }
831
832 while( is_open_ && !interrupted() ) {
833 // blocking
834 bzero((void *)&peer, sizeof(peer));
835 socklen_t addrlen = sizeof(peer); // on return it will contain the actual size of the peer address
836 int client_socket = ::accept(socket_, (struct sockaddr*)&peer, &addrlen);
837
838 BDAddressAndType remoteAddressAndType(jau::le_to_cpu(peer.l2_bdaddr), static_cast<BDAddressType>(peer.l2_bdaddr_type));
839 L2CAP_PSM c_psm = static_cast<L2CAP_PSM>(jau::le_to_cpu(peer.l2_psm));
840 L2CAP_CID c_cid = static_cast<L2CAP_CID>(jau::le_to_cpu(peer.l2_cid));
841
842 if( 0 <= client_socket )
843 {
844 DBG_PRINT("L2CAPServer::accept: Success: dev_id %u, dd[s %d, c %d], errno 0x%X %s, psm %s -> %s, cid %s -> %s, local %s -> remote %s",
845 adev_id, socket_.load(), client_socket, errno, strerror(errno),
846 to_string(psm).c_str(), to_string(c_psm).c_str(),
847 to_string(cid).c_str(), to_string(c_cid).c_str(),
849 remoteAddressAndType.toString().c_str());
850 // success
851 tid_accept = 0;
852 return std::make_unique<L2CAPClient>(adev_id, localAddressAndType, c_psm, c_cid, remoteAddressAndType, client_socket);
853 } else if( ETIMEDOUT == errno ) {
854 to_retry_count++;
856 WORDY_PRINT("L2CAPServer::accept: Timeout # %d (retry): dev_id %u, dd[s %d, c %d], errno 0x%X %s, psm %s -> %s, cid %s -> %s, local %s -> remote %s",
857 to_retry_count, adev_id, socket_.load(), client_socket, errno, strerror(errno),
858 to_string(psm).c_str(), to_string(c_psm).c_str(),
859 to_string(cid).c_str(), to_string(c_cid).c_str(),
861 remoteAddressAndType.toString().c_str());
862 continue;
863 } else {
864 WORDY_PRINT("L2CAPServer::accept: Timeout # %d (done): dev_id %u, dd[s %d, c %d], errno 0x%X %s, psm %s -> %s, cid %s -> %s, local %s -> remote %s",
865 to_retry_count, adev_id, socket_.load(), client_socket, errno, strerror(errno),
866 to_string(psm).c_str(), to_string(c_psm).c_str(),
867 to_string(cid).c_str(), to_string(c_cid).c_str(),
869 remoteAddressAndType.toString().c_str());
870 break; // exit
871 }
872
873 } else if( !interrupted() ) {
874 // EALREADY == errno || ENETUNREACH == errno || EHOSTUNREACH == errno || ..
875 IRQ_PRINT("L2CAPServer::accept: Failed: dev_id %u, dd[s %d, c %d], errno 0x%X %s, psm %s -> %s, cid %s -> %s, local %s -> remote %s",
876 adev_id, socket_.load(), client_socket, errno, strerror(errno),
877 to_string(psm).c_str(), to_string(c_psm).c_str(),
878 to_string(cid).c_str(), to_string(c_cid).c_str(),
880 remoteAddressAndType.toString().c_str());
881 break; // exit
882 }
883 }
884 // failure
885 tid_accept = 0;
886 return nullptr;
887}
888
889std::string L2CAPServer::toString() const noexcept {
890 return "L2CAPServer[dev_id "+std::to_string(adev_id)+", dd "+std::to_string(socket_)+
891 ", psm "+to_string(psm)+
892 ", cid "+to_string(cid)+
893 ", local "+localAddressAndType.toString()+
894 ", "+getStateString()+"]";
895}
#define CASE2_TO_STRING(U, V)
Definition: L2CAPComm.cpp:508
constexpr const bool SET_BT_SECURITY_POST_CONNECT
Setting BT_SECURITY within open() after bind() and before connect() causes BlueZ/Kernel to immediatel...
Definition: L2CAPComm.cpp:82
#define RWEXITCODE_ENUM(X)
Definition: L2CAPComm.cpp:497
Unique Bluetooth EUI48 address and BDAddressType tuple.
Definition: BTAddress.hpp:175
static const BDAddressAndType ANY_BREDR_DEVICE
Using EUI48::ANY_DEVICE and BDAddressType::BDADDR_BREDR to match any BREDR device.
Definition: BTAddress.hpp:178
std::string toString() const noexcept
Definition: BTTypes0.cpp:186
BTDevice represents one remote Bluetooth device.
Definition: BTDevice.hpp:81
static std::string getRWExitCodeString(const RWExitCode ec) noexcept
Definition: L2CAPComm.cpp:510
jau::snsize_t read(uint8_t *buffer, const jau::nsize_t capacity) noexcept
Generic read, w/o locking suitable for a unique ringbuffer sink.
Definition: L2CAPComm.cpp:521
std::string getStateString() const noexcept override
Definition: L2CAPComm.hpp:271
BTSecurityLevel getBTSecurityLevel() noexcept
Fetches the current BlueZ's L2CAP socket BT_SECURITY sec_level.
Definition: L2CAPComm.cpp:484
bool setBTSecurityLevel(const BTSecurityLevel sec_level) noexcept
If sec_level > BTSecurityLevel::UNSET, sets the BlueZ's L2CAP socket BT_SECURITY sec_level,...
Definition: L2CAPComm.cpp:462
L2CAPClient(const uint16_t adev_id, BDAddressAndType adapterAddressAndType, const L2CAP_PSM psm, const L2CAP_CID cid) noexcept
Constructing a non connected L2CAP channel instance for the pre-defined PSM and CID.
Definition: L2CAPComm.cpp:253
jau::snsize_t write(const uint8_t *buffer, const jau::nsize_t length) noexcept
Generic write, locking mutex_write().
Definition: L2CAPComm.cpp:643
static constexpr int number(const Defaults d) noexcept
Definition: L2CAPComm.hpp:200
std::string toString() const noexcept override
Definition: L2CAPComm.cpp:713
bool open(const BTDevice &device, const BTSecurityLevel sec_level=BTSecurityLevel::NONE) noexcept
Opens and connects the L2CAP channel, locking mutex_write().
Definition: L2CAPComm.cpp:269
RWExitCode
Exit code for read() and write() operations.
Definition: L2CAPComm.hpp:205
L2CAP client/server socket abstract base class to listen for connecting remote devices.
Definition: L2CAPComm.hpp:123
jau::sc_atomic_bool interrupted_intern
Definition: L2CAPComm.hpp:152
L2CAPComm(const uint16_t adev_id, BDAddressAndType localAddressAndType, const L2CAP_PSM psm, const L2CAP_CID cid) noexcept
Definition: L2CAPComm.cpp:147
BTSecurityLevel getBTSecurityLevelImpl(const BDAddressAndType &remoteAddressAndType) noexcept
Definition: L2CAPComm.cpp:208
const BDAddressAndType localAddressAndType
Corresponding BTAdapter local BTAddressAndType.
Definition: L2CAPComm.hpp:142
void set_interrupted_query(get_boolean_callback_t is_interrupted_cb) noexcept
The external is interrupted callback is used until close(), thereafter it is removed.
Definition: L2CAPComm.hpp:176
const L2CAP_CID cid
Corresponding L2CAP_CID for the channel.
Definition: L2CAPComm.hpp:146
jau::relaxed_atomic_int socket_
Definition: L2CAPComm.hpp:150
static int l2cap_close_dev(int dd) noexcept
Definition: L2CAPComm.cpp:142
static int l2cap_open_dev(const BDAddressAndType &adapterAddressAndType, const L2CAP_PSM psm, const L2CAP_CID cid) noexcept
Definition: L2CAPComm.cpp:97
virtual std::string getStateString() const noexcept=0
jau::sc_atomic_bool is_open_
Definition: L2CAPComm.hpp:151
std::recursive_mutex mtx_open
Definition: L2CAPComm.hpp:149
bool interrupted() const noexcept
Returns true if interrupted by internal or external cause, hence shall stop connecting and reading.
Definition: L2CAPComm.hpp:179
const L2CAP_PSM psm
Corresponding L2CAP_PSM for the channel.
Definition: L2CAPComm.hpp:144
const uint16_t adev_id
Corresponding BTAdapter device id.
Definition: L2CAPComm.hpp:140
bool setBTSecurityLevelImpl(const BTSecurityLevel sec_level, const BDAddressAndType &remoteAddressAndType) noexcept
Definition: L2CAPComm.cpp:156
static L2CAPEnv & get() noexcept
Definition: L2CAPComm.hpp:104
L2CAPServer(const uint16_t adev_id, BDAddressAndType localAddressAndType, const L2CAP_PSM psm, const L2CAP_CID cid) noexcept
Definition: L2CAPComm.cpp:726
bool close() noexcept override
Closing the L2CAP socket, see specializations.
Definition: L2CAPComm.hpp:333
std::unique_ptr< L2CAPClient > accept() noexcept
Definition: L2CAPComm.cpp:818
bool open() noexcept
Definition: L2CAPComm.cpp:730
std::string toString() const noexcept override
Definition: L2CAPComm.cpp:889
std::string getStateString() const noexcept override
Definition: L2CAPComm.hpp:337
#define ERR_PRINT(...)
Use for unconditional error messages, prefix '[elapsed_time] Error @ FILE:LINE FUNC: '.
Definition: debug.hpp:109
#define WORDY_PRINT(...)
Use for environment-variable environment::VERBOSE conditional verbose messages, prefix '[elapsed_time...
Definition: debug.hpp:68
#define ABORT(...)
Use for unconditional ::abort() call with given messages, prefix '[elapsed_time] ABORT @ file:line fu...
Definition: debug.hpp:101
#define PERF_TS_T0()
Definition: debug.hpp:79
#define DBG_PRINT(...)
Use for environment-variable environment::DEBUG conditional debug messages, prefix '[elapsed_time] De...
Definition: debug.hpp:52
#define IRQ_PRINT(...)
Use for unconditional interruption messages, prefix '[elapsed_time] Interrupted @ FILE:LINE FUNC: '.
Definition: debug.hpp:115
#define PERF_TS_TD(m)
Definition: debug.hpp:80
constexpr uint16_t le_to_cpu(uint16_t const l) noexcept
Definition: byte_util.hpp:396
constexpr uint16_t cpu_to_le(uint16_t const h) noexcept
Definition: byte_util.hpp:405
std::string to_string(const alphabet &v) noexcept
Definition: base_codec.hpp:97
constexpr const bool USE_LINUX_BT_SECURITY
Definition: DBTConst.hpp:60
std::string to_string(const DiscoveryPolicy v) noexcept
Definition: BTAdapter.cpp:58
BDAddressType
BT Core Spec v5.2: Vol 3, Part C Generic Access Profile (GAP): 15.1.1.1 Public Bluetooth address.
Definition: BTAddress.hpp:60
L2CAP_PSM
Protocol Service Multiplexers (PSM) Assigned numbers https://www.bluetooth.com/specifications/assigne...
Definition: BTTypes0.hpp:514
BTSecurityLevel
Bluetooth Security Level.
Definition: BTTypes0.hpp:267
constexpr uint8_t number(const DiscoveryPolicy rhs) noexcept
Definition: BTAdapter.hpp:100
@ UNSET
Security Level not set, value 0.
@ NONE
No encryption and no authentication.
uint_fast32_t nsize_t
Natural 'size_t' alternative using uint_fast32_t as its natural sized type.
Definition: int_types.hpp:53
int_fast32_t snsize_t
Natural 'ssize_t' alternative using int_fast32_t as its natural sized type.
Definition: int_types.hpp:65
__pack(...): Produces MSVC, clang and gcc compatible lead-in and -out macros.
Definition: backtrace.hpp:32
CXX_ALWAYS_INLINE _Tp load() const noexcept
CXX_ALWAYS_INLINE bool compare_exchange_strong(_Tp &__e, _Tp __i) noexcept