libstdc++: shared_mutex Source File (original) (raw)

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29#ifndef _GLIBCXX_SHARED_MUTEX

30#define _GLIBCXX_SHARED_MUTEX 1

31

32#pragma GCC system_header

33

35

36#if __cplusplus >= 201402L

37

43

44#define __glibcxx_want_shared_mutex

45#define __glibcxx_want_shared_timed_mutex

47

48#if ! (_GLIBCXX_USE_PTHREAD_RWLOCK_T && _GTHREAD_USE_MUTEX_TIMEDLOCK)

50#endif

51

52namespace std _GLIBCXX_VISIBILITY(default)

53{

54_GLIBCXX_BEGIN_NAMESPACE_VERSION

55

56

57

58

59

60

61#ifdef _GLIBCXX_HAS_GTHREADS

62

63#ifdef __cpp_lib_shared_mutex

64 class shared_mutex;

65#endif

66

67 class shared_timed_mutex;

68

69

70

71#if _GLIBCXX_USE_PTHREAD_RWLOCK_T

72#ifdef __gthrw

73#define _GLIBCXX_GTHRW(name) \

74 __gthrw(pthread_ ## name); \

75 static inline int \

76 __glibcxx_ ## name (pthread_rwlock_t *__rwlock) \

77 { \

78 if (__gthread_active_p ()) \

79 return __gthrw_(pthread_ ## name) (__rwlock); \

80 else \

81 return 0; \

82 }

83 _GLIBCXX_GTHRW(rwlock_rdlock)

84 _GLIBCXX_GTHRW(rwlock_tryrdlock)

85 _GLIBCXX_GTHRW(rwlock_wrlock)

86 _GLIBCXX_GTHRW(rwlock_trywrlock)

87 _GLIBCXX_GTHRW(rwlock_unlock)

88# ifndef PTHREAD_RWLOCK_INITIALIZER

89 _GLIBCXX_GTHRW(rwlock_destroy)

90 __gthrw(pthread_rwlock_init);

91 static inline int

92 __glibcxx_rwlock_init (pthread_rwlock_t *__rwlock)

93 {

94 if (__gthread_active_p ())

95 return __gthrw_(pthread_rwlock_init) (__rwlock, NULL);

96 else

97 return 0;

98 }

99# endif

100# if _GTHREAD_USE_MUTEX_TIMEDLOCK

101 __gthrw(pthread_rwlock_timedrdlock);

102 static inline int

103 __glibcxx_rwlock_timedrdlock (pthread_rwlock_t *__rwlock,

104 const timespec *__ts)

105 {

106 if (__gthread_active_p ())

107 return __gthrw_(pthread_rwlock_timedrdlock) (__rwlock, __ts);

108 else

109 return 0;

110 }

111 __gthrw(pthread_rwlock_timedwrlock);

112 static inline int

113 __glibcxx_rwlock_timedwrlock (pthread_rwlock_t *__rwlock,

114 const timespec *__ts)

115 {

116 if (__gthread_active_p ())

117 return __gthrw_(pthread_rwlock_timedwrlock) (__rwlock, __ts);

118 else

119 return 0;

120 }

121# endif

122#else

123 static inline int

124 __glibcxx_rwlock_rdlock (pthread_rwlock_t *__rwlock)

125 { return pthread_rwlock_rdlock (__rwlock); }

126 static inline int

127 __glibcxx_rwlock_tryrdlock (pthread_rwlock_t *__rwlock)

128 { return pthread_rwlock_tryrdlock (__rwlock); }

129 static inline int

130 __glibcxx_rwlock_wrlock (pthread_rwlock_t *__rwlock)

131 { return pthread_rwlock_wrlock (__rwlock); }

132 static inline int

133 __glibcxx_rwlock_trywrlock (pthread_rwlock_t *__rwlock)

134 { return pthread_rwlock_trywrlock (__rwlock); }

135 static inline int

136 __glibcxx_rwlock_unlock (pthread_rwlock_t *__rwlock)

137 { return pthread_rwlock_unlock (__rwlock); }

138 static inline int

139 __glibcxx_rwlock_destroy(pthread_rwlock_t *__rwlock)

140 { return pthread_rwlock_destroy (__rwlock); }

141 static inline int

142 __glibcxx_rwlock_init(pthread_rwlock_t *__rwlock)

143 { return pthread_rwlock_init (__rwlock, NULL); }

144# if _GTHREAD_USE_MUTEX_TIMEDLOCK

145 static inline int

146 __glibcxx_rwlock_timedrdlock (pthread_rwlock_t *__rwlock,

147 const timespec *__ts)

148 { return pthread_rwlock_timedrdlock (__rwlock, __ts); }

149 static inline int

150 __glibcxx_rwlock_timedwrlock (pthread_rwlock_t *__rwlock,

151 const timespec *__ts)

152 { return pthread_rwlock_timedwrlock (__rwlock, __ts); }

153# endif

154#endif

155

156

157 class __shared_mutex_pthread

158 {

159 friend class shared_timed_mutex;

160

161#ifdef PTHREAD_RWLOCK_INITIALIZER

162 pthread_rwlock_t _M_rwlock = PTHREAD_RWLOCK_INITIALIZER;

163

164 public:

165 __shared_mutex_pthread() = default;

166 ~__shared_mutex_pthread() = default;

167#else

168 pthread_rwlock_t _M_rwlock;

169

170 public:

171 __shared_mutex_pthread()

172 {

173 int __ret = __glibcxx_rwlock_init(&_M_rwlock);

174 if (__ret == ENOMEM)

175 __throw_bad_alloc();

176 else if (__ret == EAGAIN)

177 __throw_system_error(int(errc::resource_unavailable_try_again));

178 else if (__ret == EPERM)

179 __throw_system_error(int(errc::operation_not_permitted));

180

181 __glibcxx_assert(__ret == 0);

182 }

183

184 ~__shared_mutex_pthread()

185 {

186 int __ret __attribute((__unused__)) = __glibcxx_rwlock_destroy(&_M_rwlock);

187

188 __glibcxx_assert(__ret == 0);

189 }

190#endif

191

192 __shared_mutex_pthread(const __shared_mutex_pthread&) = delete;

193 __shared_mutex_pthread& operator=(const __shared_mutex_pthread&) = delete;

194

195 void

197 {

198 int __ret = __glibcxx_rwlock_wrlock(&_M_rwlock);

199 if (__ret == EDEADLK)

200 __throw_system_error(int(errc::resource_deadlock_would_occur));

201

202 __glibcxx_assert(__ret == 0);

203 }

204

205 bool

207 {

208 int __ret = __glibcxx_rwlock_trywrlock(&_M_rwlock);

209 if (__ret == EBUSY) return false;

210

211 __glibcxx_assert(__ret == 0);

212 return true;

213 }

214

215 void

216 unlock()

217 {

218 int __ret __attribute((__unused__)) = __glibcxx_rwlock_unlock(&_M_rwlock);

219

220 __glibcxx_assert(__ret == 0);

221 }

222

223

224

225 void

226 lock_shared()

227 {

228 int __ret;

229

230

231

232

233 do

234 __ret = __glibcxx_rwlock_rdlock(&_M_rwlock);

235 while (__ret == EAGAIN);

236 if (__ret == EDEADLK)

237 __throw_system_error(int(errc::resource_deadlock_would_occur));

238

239 __glibcxx_assert(__ret == 0);

240 }

241

242 bool

243 try_lock_shared()

244 {

245 int __ret = __glibcxx_rwlock_tryrdlock(&_M_rwlock);

246

247

248

249 if (__ret == EBUSY || __ret == EAGAIN) return false;

250

251 __glibcxx_assert(__ret == 0);

252 return true;

253 }

254

255 void

256 unlock_shared()

257 {

258 unlock();

259 }

260

261 void* native_handle() { return &_M_rwlock; }

262 };

263#endif

264

265#if ! (_GLIBCXX_USE_PTHREAD_RWLOCK_T && _GTHREAD_USE_MUTEX_TIMEDLOCK)

266

267 class __shared_mutex_cv

268 {

269 friend class shared_timed_mutex;

270

271

272

273

274

275

276

277

278

279

280

281

282

283

284

285

286

287

288

289

290

291

292

293

294

295

296

297 mutex _M_mut;

298

299 condition_variable _M_gate1;

300

301 condition_variable _M_gate2;

302

303 unsigned _M_state;

304

305 static constexpr unsigned _S_write_entered

306 = 1U << (sizeof(unsigned)*__CHAR_BIT__ - 1);

307 static constexpr unsigned _S_max_readers = ~_S_write_entered;

308

309

310 bool _M_write_entered() const { return _M_state & _S_write_entered; }

311

312

313 unsigned _M_readers() const { return _M_state & _S_max_readers; }

314

315 public:

316 __shared_mutex_cv() : _M_state(0) {}

317

318 ~__shared_mutex_cv()

319 {

320 __glibcxx_assert( _M_state == 0 );

321 }

322

323 __shared_mutex_cv(const __shared_mutex_cv&) = delete;

324 __shared_mutex_cv& operator=(const __shared_mutex_cv&) = delete;

325

326

327

328 void

330 {

331 unique_lock __lk(_M_mut);

332

333 _M_gate1.wait(__lk, [=]{ return !_M_write_entered(); });

334 _M_state |= _S_write_entered;

335

336 _M_gate2.wait(__lk, [=]{ return _M_readers() == 0; });

337 }

338

339 bool

341 {

342 unique_lock __lk(_M_mut, try_to_lock);

343 if (__lk.owns_lock() && _M_state == 0)

344 {

345 _M_state = _S_write_entered;

346 return true;

347 }

348 return false;

349 }

350

351 void

352 unlock()

353 {

354 lock_guard __lk(_M_mut);

355 __glibcxx_assert( _M_write_entered() );

356 _M_state = 0;

357

358

359 _M_gate1.notify_all();

360 }

361

362

363

364 void

365 lock_shared()

366 {

367 unique_lock __lk(_M_mut);

368 _M_gate1.wait(__lk, [=]{ return _M_state < _S_max_readers; });

369 ++_M_state;

370 }

371

372 bool

373 try_lock_shared()

374 {

375 unique_lock __lk(_M_mut, try_to_lock);

376 if (!__lk.owns_lock())

377 return false;

378 if (_M_state < _S_max_readers)

379 {

380 ++_M_state;

381 return true;

382 }

383 return false;

384 }

385

386 void

387 unlock_shared()

388 {

389 lock_guard __lk(_M_mut);

390 __glibcxx_assert( _M_readers() > 0 );

391 auto __prev = _M_state--;

392 if (_M_write_entered())

393 {

394

395 if (_M_readers() == 0)

396 _M_gate2.notify_one();

397

398

399

400 }

401 else

402 {

403

404 if (__prev == _S_max_readers)

405 _M_gate1.notify_one();

406 }

407 }

408 };

409#endif

410

411

412#ifdef __cpp_lib_shared_mutex

413

414 class shared_mutex

415 {

416 public:

417 shared_mutex() = default;

418 ~shared_mutex() = default;

419

420 shared_mutex(const shared_mutex&) = delete;

421 shared_mutex& operator=(const shared_mutex&) = delete;

422

423

424

425 void lock() { _M_impl.lock(); }

426 [[nodiscard]] bool try_lock() { return _M_impl.try_lock(); }

427 void unlock() { _M_impl.unlock(); }

428

429

430

431 void lock_shared() { _M_impl.lock_shared(); }

432 [[nodiscard]] bool try_lock_shared() { return _M_impl.try_lock_shared(); }

433 void unlock_shared() { _M_impl.unlock_shared(); }

434

435#if _GLIBCXX_USE_PTHREAD_RWLOCK_T

436 typedef void* native_handle_type;

437 native_handle_type native_handle() { return _M_impl.native_handle(); }

438

439 private:

440 __shared_mutex_pthread _M_impl;

441#else

442 private:

443 __shared_mutex_cv _M_impl;

444#endif

445 };

446#endif

447

448

449#if _GLIBCXX_USE_PTHREAD_RWLOCK_T && _GTHREAD_USE_MUTEX_TIMEDLOCK

450 using __shared_timed_mutex_base = __shared_mutex_pthread;

451#else

452 using __shared_timed_mutex_base = __shared_mutex_cv;

453#endif

454

455

456

458 : private __shared_timed_mutex_base

459 {

460 using _Base = __shared_timed_mutex_base;

461

462

463#ifdef _GLIBCXX_USE_PTHREAD_RWLOCK_CLOCKLOCK

465#else

467#endif

468

469 public:

472

475

476

477

478 void lock() { _Base::lock(); }

479 _GLIBCXX_NODISCARD bool try_lock() { return _Base::try_lock(); }

480 void unlock() { _Base::unlock(); }

481

482 template<typename _Rep, typename _Period>

483 _GLIBCXX_NODISCARD

484 bool

486 {

487 auto __rt = chrono::duration_cast<__clock_t::duration>(__rtime);

489 ++__rt;

490 return try_lock_until(__clock_t::now() + __rt);

491 }

492

493

494

495 void lock_shared() { _Base::lock_shared(); }

496 _GLIBCXX_NODISCARD

497 bool try_lock_shared() { return _Base::try_lock_shared(); }

498 void unlock_shared() { _Base::unlock_shared(); }

499

500 template<typename _Rep, typename _Period>

501 _GLIBCXX_NODISCARD

502 bool

504 {

505 auto __rt = chrono::duration_cast<__clock_t::duration>(__rtime);

507 ++__rt;

508 return try_lock_shared_until(__clock_t::now() + __rt);

509 }

510

511#if _GLIBCXX_USE_PTHREAD_RWLOCK_T && _GTHREAD_USE_MUTEX_TIMEDLOCK

512

513

514

515 template<typename _Duration>

516 _GLIBCXX_NODISCARD

517 bool

519 _Duration>& __atime)

520 {

521 auto __s = chrono::time_point_castchrono::seconds(__atime);

522 auto __ns = chrono::duration_castchrono::nanoseconds(__atime - __s);

523

524 __gthread_time_t __ts =

525 {

526 static_caststd::time\_t\(__s.time_since_epoch().count()),

527 static_cast<long>(__ns.count())

528 };

529

530 int __ret = __glibcxx_rwlock_timedwrlock(&_M_rwlock, &__ts);

531

532

533 if (__ret == ETIMEDOUT || __ret == EDEADLK)

534 return false;

535

536 __glibcxx_assert(__ret == 0);

537 return true;

538 }

539

540#ifdef _GLIBCXX_USE_PTHREAD_RWLOCK_CLOCKLOCK

541 template<typename _Duration>

542 _GLIBCXX_NODISCARD

543 bool

545 _Duration>& __atime)

546 {

547 auto __s = chrono::time_point_castchrono::seconds(__atime);

548 auto __ns = chrono::duration_castchrono::nanoseconds(__atime - __s);

549

550 __gthread_time_t __ts =

551 {

552 static_caststd::time\_t\(__s.time_since_epoch().count()),

553 static_cast<long>(__ns.count())

554 };

555

556 int __ret = pthread_rwlock_clockwrlock(&_M_rwlock, CLOCK_MONOTONIC,

557 &__ts);

558

559

560 if (__ret == ETIMEDOUT || __ret == EDEADLK)

561 return false;

562

563 __glibcxx_assert(__ret == 0);

564 return true;

565 }

566#endif

567

568 template<typename _Clock, typename _Duration>

569 _GLIBCXX_NODISCARD

570 bool

572 {

573#if __cplusplus > 201703L

574 static_assert(chrono::is_clock_v<_Clock>);

575#endif

576

577

578

579 typename _Clock::time_point __now = _Clock::now();

580 do {

581 auto __rtime = __atime - __now;

582 if (try_lock_for(__rtime))

583 return true;

584 __now = _Clock::now();

585 } while (__atime > __now);

586 return false;

587 }

588

589

590

591 template<typename _Duration>

592 _GLIBCXX_NODISCARD

593 bool

595 _Duration>& __atime)

596 {

597 auto __s = chrono::time_point_castchrono::seconds(__atime);

598 auto __ns = chrono::duration_castchrono::nanoseconds(__atime - __s);

599

600 __gthread_time_t __ts =

601 {

602 static_caststd::time\_t\(__s.time_since_epoch().count()),

603 static_cast<long>(__ns.count())

604 };

605

606 int __ret;

607

608

609

610

611

612

613

614

615

616

617

618

619

620 do

621 __ret = __glibcxx_rwlock_timedrdlock(&_M_rwlock, &__ts);

622 while (__ret == EAGAIN || __ret == EDEADLK);

623 if (__ret == ETIMEDOUT)

624 return false;

625

626 __glibcxx_assert(__ret == 0);

627 return true;

628 }

629

630#ifdef _GLIBCXX_USE_PTHREAD_RWLOCK_CLOCKLOCK

631 template<typename _Duration>

632 _GLIBCXX_NODISCARD

633 bool

635 _Duration>& __atime)

636 {

637 auto __s = chrono::time_point_castchrono::seconds(__atime);

638 auto __ns = chrono::duration_castchrono::nanoseconds(__atime - __s);

639

640 __gthread_time_t __ts =

641 {

642 static_caststd::time\_t\(__s.time_since_epoch().count()),

643 static_cast<long>(__ns.count())

644 };

645

646 int __ret = pthread_rwlock_clockrdlock(&_M_rwlock, CLOCK_MONOTONIC,

647 &__ts);

648

649

650 if (__ret == ETIMEDOUT || __ret == EDEADLK)

651 return false;

652

653 __glibcxx_assert(__ret == 0);

654 return true;

655 }

656#endif

657

658 template<typename _Clock, typename _Duration>

659 _GLIBCXX_NODISCARD

660 bool

662 _Duration>& __atime)

663 {

664#if __cplusplus > 201703L

665 static_assert(chrono::is_clock_v<_Clock>);

666#endif

667

668

669

670 typename _Clock::time_point __now = _Clock::now();

671 do {

672 auto __rtime = __atime - __now;

673 if (try_lock_shared_for(__rtime))

674 return true;

675 __now = _Clock::now();

676 } while (__atime > __now);

677 return false;

678 }

679

680#else

681

682

683

684 template<typename _Clock, typename _Duration>

685 _GLIBCXX_NODISCARD

686 bool

688 {

690 if (!_M_gate1.wait_until(__lk, __abs_time,

691 [=]{ return !_M_write_entered(); }))

692 {

693 return false;

694 }

695 _M_state |= _S_write_entered;

696 if (!_M_gate2.wait_until(__lk, __abs_time,

697 [=]{ return _M_readers() == 0; }))

698 {

699 _M_state ^= _S_write_entered;

700

701 _M_gate1.notify_all();

702 return false;

703 }

704 return true;

705 }

706

707

708

709 template <typename _Clock, typename _Duration>

710 _GLIBCXX_NODISCARD

711 bool

713 _Duration>& __abs_time)

714 {

716 if (!_M_gate1.wait_until(__lk, __abs_time,

717 [=]{ return _M_state < _S_max_readers; }))

718 {

719 return false;

720 }

721 ++_M_state;

722 return true;

723 }

724

725#endif

726 };

727#endif

728

729

730 template<typename _Mutex>

732 {

733 public:

734 typedef _Mutex mutex_type;

735

736

737

738 shared_lock() noexcept : _M_pm(nullptr), _M_owns(false) { }

739

740 explicit

743 { __m.lock_shared(); }

744

747

749 : _M_pm(std::__addressof(__m)), _M_owns(__m.try_lock_shared()) { }

750

753

754 template<typename _Clock, typename _Duration>

758 _M_owns(__m.try_lock_shared_until(__abs_time)) { }

759

760 template<typename _Rep, typename _Period>

764 _M_owns(__m.try_lock_shared_for(__rel_time)) { }

765

767 {

768 if (_M_owns)

769 _M_pm->unlock_shared();

770 }

771

774

776 { swap(__sl); }

777

780 {

782 return *this;

783 }

784

785 void

786 lock()

787 {

788 _M_lockable();

789 _M_pm->lock_shared();

790 _M_owns = true;

791 }

792

793 _GLIBCXX_NODISCARD

794 bool

795 try_lock()

796 {

797 _M_lockable();

798 return _M_owns = _M_pm->try_lock_shared();

799 }

800

801 template<typename _Rep, typename _Period>

802 _GLIBCXX_NODISCARD

803 bool

805 {

806 _M_lockable();

807 return _M_owns = _M_pm->try_lock_shared_for(__rel_time);

808 }

809

810 template<typename _Clock, typename _Duration>

811 _GLIBCXX_NODISCARD

812 bool

814 {

815 _M_lockable();

816 return _M_owns = _M_pm->try_lock_shared_until(__abs_time);

817 }

818

819 void

820 unlock()

821 {

822 if (!_M_owns)

823 __throw_system_error(int(errc::operation_not_permitted));

824 _M_pm->unlock_shared();

825 _M_owns = false;

826 }

827

828

829

830 void

832 {

833 std::swap(_M_pm, __u._M_pm);

834 std::swap(_M_owns, __u._M_owns);

835 }

836

837 mutex_type*

838 release() noexcept

839 {

840 _M_owns = false;

841 return std::__exchange(_M_pm, nullptr);

842 }

843

844

845

846 _GLIBCXX_NODISCARD

847 bool owns_lock() const noexcept { return _M_owns; }

848

849 explicit operator bool() const noexcept { return _M_owns; }

850

851 _GLIBCXX_NODISCARD

852 mutex_type* mutex() const noexcept { return _M_pm; }

853

854 private:

855 void

856 _M_lockable() const

857 {

858 if (_M_pm == nullptr)

859 __throw_system_error(int(errc::operation_not_permitted));

860 if (_M_owns)

861 __throw_system_error(int(errc::resource_deadlock_would_occur));

862 }

863

864 mutex_type* _M_pm;

865 bool _M_owns;

866 };

867

868

869

870 template<typename _Mutex>

871 void

873 { __x.swap(__y); }

874

875

876_GLIBCXX_END_NAMESPACE_VERSION

877}

878

879#endif

880

881#endif

constexpr std::remove_reference< _Tp >::type && move(_Tp &&__t) noexcept

Convert a value to an rvalue.

constexpr _Tp * __addressof(_Tp &__r) noexcept

Same as C++11 std::addressof.

void lock(_L1 &__l1, _L2 &__l2, _L3 &... __l3)

Generic lock.

int try_lock(_L1 &__l1, _L2 &__l2, _L3 &... __l3)

Generic try_lock.

ISO C++ entities toplevel namespace is std.

The standard shared timed mutex type.

chrono::duration represents a distance between two points in time

chrono::time_point represents a point in time as measured by a clock

Do not acquire ownership of the mutex.

Try to acquire ownership of the mutex without blocking.

Assume the calling thread has already obtained mutex ownership and manage it.

A movable scoped lock type.