File indexing completed on 2025-01-18 09:29:12
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011 #ifndef BOOST_ASIO_THREAD_POOL_HPP
0012 #define BOOST_ASIO_THREAD_POOL_HPP
0013
0014 #if defined(_MSC_VER) && (_MSC_VER >= 1200)
0015 # pragma once
0016 #endif
0017
0018 #include <boost/asio/detail/config.hpp>
0019 #include <boost/asio/detail/atomic_count.hpp>
0020 #include <boost/asio/detail/scheduler.hpp>
0021 #include <boost/asio/detail/thread_group.hpp>
0022 #include <boost/asio/execution.hpp>
0023 #include <boost/asio/execution_context.hpp>
0024
0025 #include <boost/asio/detail/push_options.hpp>
0026
0027 namespace boost {
0028 namespace asio {
0029 namespace detail {
0030 struct thread_pool_bits
0031 {
0032 static constexpr unsigned int blocking_never = 1;
0033 static constexpr unsigned int blocking_always = 2;
0034 static constexpr unsigned int blocking_mask = 3;
0035 static constexpr unsigned int relationship_continuation = 4;
0036 static constexpr unsigned int outstanding_work_tracked = 8;
0037 };
0038 }
0039
0040
0041
0042
0043
0044
0045
0046
0047
0048
0049
0050
0051
0052
0053
0054
0055
0056
0057
0058
0059
0060
0061
0062
0063
0064
0065
0066
0067
0068
0069
0070
0071
0072
0073
0074
0075 class thread_pool
0076 : public execution_context
0077 {
0078 public:
0079 template <typename Allocator, unsigned int Bits>
0080 class basic_executor_type;
0081
0082 template <typename Allocator, unsigned int Bits>
0083 friend class basic_executor_type;
0084
0085
0086 typedef basic_executor_type<std::allocator<void>, 0> executor_type;
0087
0088 #if !defined(BOOST_ASIO_NO_TS_EXECUTORS)
0089
0090 BOOST_ASIO_DECL thread_pool();
0091 #endif
0092
0093
0094 BOOST_ASIO_DECL thread_pool(std::size_t num_threads);
0095
0096
0097
0098
0099
0100 BOOST_ASIO_DECL ~thread_pool();
0101
0102
0103 executor_type get_executor() noexcept;
0104
0105
0106 executor_type executor() noexcept;
0107
0108
0109
0110
0111
0112
0113 BOOST_ASIO_DECL void stop();
0114
0115
0116
0117
0118
0119
0120
0121 BOOST_ASIO_DECL void attach();
0122
0123
0124
0125
0126
0127
0128
0129 BOOST_ASIO_DECL void join();
0130
0131
0132
0133
0134
0135
0136
0137 BOOST_ASIO_DECL void wait();
0138
0139 private:
0140 thread_pool(const thread_pool&) = delete;
0141 thread_pool& operator=(const thread_pool&) = delete;
0142
0143 struct thread_function;
0144
0145
0146 BOOST_ASIO_DECL detail::scheduler& add_scheduler(detail::scheduler* s);
0147
0148
0149 detail::scheduler& scheduler_;
0150
0151
0152 detail::thread_group threads_;
0153
0154
0155 detail::atomic_count num_threads_;
0156 };
0157
0158
0159 template <typename Allocator, unsigned int Bits>
0160 class thread_pool::basic_executor_type : detail::thread_pool_bits
0161 {
0162 public:
0163
0164 basic_executor_type(const basic_executor_type& other) noexcept
0165 : pool_(other.pool_),
0166 allocator_(other.allocator_),
0167 bits_(other.bits_)
0168 {
0169 if (Bits & outstanding_work_tracked)
0170 if (pool_)
0171 pool_->scheduler_.work_started();
0172 }
0173
0174
0175 basic_executor_type(basic_executor_type&& other) noexcept
0176 : pool_(other.pool_),
0177 allocator_(static_cast<Allocator&&>(other.allocator_)),
0178 bits_(other.bits_)
0179 {
0180 if (Bits & outstanding_work_tracked)
0181 other.pool_ = 0;
0182 }
0183
0184
0185 ~basic_executor_type() noexcept
0186 {
0187 if (Bits & outstanding_work_tracked)
0188 if (pool_)
0189 pool_->scheduler_.work_finished();
0190 }
0191
0192
0193 basic_executor_type& operator=(const basic_executor_type& other) noexcept;
0194
0195
0196 basic_executor_type& operator=(basic_executor_type&& other) noexcept;
0197
0198 #if !defined(GENERATING_DOCUMENTATION)
0199 private:
0200 friend struct boost_asio_require_fn::impl;
0201 friend struct boost_asio_prefer_fn::impl;
0202 #endif
0203
0204
0205
0206
0207
0208
0209
0210
0211
0212
0213
0214 constexpr basic_executor_type<Allocator,
0215 BOOST_ASIO_UNSPECIFIED(Bits & ~blocking_mask)>
0216 require(execution::blocking_t::possibly_t) const
0217 {
0218 return basic_executor_type<Allocator, Bits & ~blocking_mask>(
0219 pool_, allocator_, bits_ & ~blocking_mask);
0220 }
0221
0222
0223
0224
0225
0226
0227
0228
0229
0230
0231
0232 constexpr basic_executor_type<Allocator,
0233 BOOST_ASIO_UNSPECIFIED((Bits & ~blocking_mask) | blocking_always)>
0234 require(execution::blocking_t::always_t) const
0235 {
0236 return basic_executor_type<Allocator,
0237 BOOST_ASIO_UNSPECIFIED((Bits & ~blocking_mask) | blocking_always)>(
0238 pool_, allocator_, bits_ & ~blocking_mask);
0239 }
0240
0241
0242
0243
0244
0245
0246
0247
0248
0249
0250
0251 constexpr basic_executor_type<Allocator,
0252 BOOST_ASIO_UNSPECIFIED(Bits & ~blocking_mask)>
0253 require(execution::blocking_t::never_t) const
0254 {
0255 return basic_executor_type<Allocator, Bits & ~blocking_mask>(
0256 pool_, allocator_, (bits_ & ~blocking_mask) | blocking_never);
0257 }
0258
0259
0260
0261
0262
0263
0264
0265
0266
0267
0268
0269 constexpr basic_executor_type require(execution::relationship_t::fork_t) const
0270 {
0271 return basic_executor_type(pool_,
0272 allocator_, bits_ & ~relationship_continuation);
0273 }
0274
0275
0276
0277
0278
0279
0280
0281
0282
0283
0284
0285 constexpr basic_executor_type require(
0286 execution::relationship_t::continuation_t) const
0287 {
0288 return basic_executor_type(pool_,
0289 allocator_, bits_ | relationship_continuation);
0290 }
0291
0292
0293
0294
0295
0296
0297
0298
0299
0300
0301
0302 constexpr basic_executor_type<Allocator,
0303 BOOST_ASIO_UNSPECIFIED(Bits | outstanding_work_tracked)>
0304 require(execution::outstanding_work_t::tracked_t) const
0305 {
0306 return basic_executor_type<Allocator, Bits | outstanding_work_tracked>(
0307 pool_, allocator_, bits_);
0308 }
0309
0310
0311
0312
0313
0314
0315
0316
0317
0318
0319
0320 constexpr basic_executor_type<Allocator,
0321 BOOST_ASIO_UNSPECIFIED(Bits & ~outstanding_work_tracked)>
0322 require(execution::outstanding_work_t::untracked_t) const
0323 {
0324 return basic_executor_type<Allocator, Bits & ~outstanding_work_tracked>(
0325 pool_, allocator_, bits_);
0326 }
0327
0328
0329
0330
0331
0332
0333
0334
0335
0336
0337
0338 template <typename OtherAllocator>
0339 constexpr basic_executor_type<OtherAllocator, Bits>
0340 require(execution::allocator_t<OtherAllocator> a) const
0341 {
0342 return basic_executor_type<OtherAllocator, Bits>(
0343 pool_, a.value(), bits_);
0344 }
0345
0346
0347
0348
0349
0350
0351
0352
0353
0354
0355
0356 constexpr basic_executor_type<std::allocator<void>, Bits>
0357 require(execution::allocator_t<void>) const
0358 {
0359 return basic_executor_type<std::allocator<void>, Bits>(
0360 pool_, std::allocator<void>(), bits_);
0361 }
0362
0363 #if !defined(GENERATING_DOCUMENTATION)
0364 private:
0365 friend struct boost_asio_query_fn::impl;
0366 friend struct boost::asio::execution::detail::mapping_t<0>;
0367 friend struct boost::asio::execution::detail::outstanding_work_t<0>;
0368 #endif
0369
0370
0371
0372
0373
0374
0375
0376
0377
0378
0379
0380
0381 static constexpr execution::mapping_t query(execution::mapping_t) noexcept
0382 {
0383 return execution::mapping.thread;
0384 }
0385
0386
0387
0388
0389
0390
0391
0392
0393
0394
0395
0396 thread_pool& query(execution::context_t) const noexcept
0397 {
0398 return *pool_;
0399 }
0400
0401
0402
0403
0404
0405
0406
0407
0408
0409
0410
0411
0412 constexpr execution::blocking_t query(execution::blocking_t) const noexcept
0413 {
0414 return (bits_ & blocking_never)
0415 ? execution::blocking_t(execution::blocking.never)
0416 : ((Bits & blocking_always)
0417 ? execution::blocking_t(execution::blocking.always)
0418 : execution::blocking_t(execution::blocking.possibly));
0419 }
0420
0421
0422
0423
0424
0425
0426
0427
0428
0429
0430
0431
0432 constexpr execution::relationship_t query(
0433 execution::relationship_t) const noexcept
0434 {
0435 return (bits_ & relationship_continuation)
0436 ? execution::relationship_t(execution::relationship.continuation)
0437 : execution::relationship_t(execution::relationship.fork);
0438 }
0439
0440
0441
0442
0443
0444
0445
0446
0447
0448
0449
0450
0451 static constexpr execution::outstanding_work_t query(
0452 execution::outstanding_work_t) noexcept
0453 {
0454 return (Bits & outstanding_work_tracked)
0455 ? execution::outstanding_work_t(execution::outstanding_work.tracked)
0456 : execution::outstanding_work_t(execution::outstanding_work.untracked);
0457 }
0458
0459
0460
0461
0462
0463
0464
0465
0466
0467
0468
0469 template <typename OtherAllocator>
0470 constexpr Allocator query(
0471 execution::allocator_t<OtherAllocator>) const noexcept
0472 {
0473 return allocator_;
0474 }
0475
0476
0477
0478
0479
0480
0481
0482
0483
0484
0485
0486 constexpr Allocator query(execution::allocator_t<void>) const noexcept
0487 {
0488 return allocator_;
0489 }
0490
0491
0492
0493
0494
0495
0496
0497
0498
0499
0500
0501 std::size_t query(execution::occupancy_t) const noexcept
0502 {
0503 return static_cast<std::size_t>(pool_->num_threads_);
0504 }
0505
0506 public:
0507
0508
0509
0510
0511
0512 bool running_in_this_thread() const noexcept;
0513
0514
0515
0516
0517
0518 friend bool operator==(const basic_executor_type& a,
0519 const basic_executor_type& b) noexcept
0520 {
0521 return a.pool_ == b.pool_
0522 && a.allocator_ == b.allocator_
0523 && a.bits_ == b.bits_;
0524 }
0525
0526
0527
0528
0529
0530 friend bool operator!=(const basic_executor_type& a,
0531 const basic_executor_type& b) noexcept
0532 {
0533 return a.pool_ != b.pool_
0534 || a.allocator_ != b.allocator_
0535 || a.bits_ != b.bits_;
0536 }
0537
0538
0539 template <typename Function>
0540 void execute(Function&& f) const
0541 {
0542 this->do_execute(static_cast<Function&&>(f),
0543 integral_constant<bool, (Bits & blocking_always) != 0>());
0544 }
0545
0546 public:
0547 #if !defined(BOOST_ASIO_NO_TS_EXECUTORS)
0548
0549 thread_pool& context() const noexcept;
0550
0551
0552
0553
0554
0555
0556
0557 void on_work_started() const noexcept;
0558
0559
0560
0561
0562
0563
0564
0565 void on_work_finished() const noexcept;
0566
0567
0568
0569
0570
0571
0572
0573
0574
0575
0576
0577
0578
0579
0580
0581 template <typename Function, typename OtherAllocator>
0582 void dispatch(Function&& f, const OtherAllocator& a) const;
0583
0584
0585
0586
0587
0588
0589
0590
0591
0592
0593
0594
0595
0596
0597 template <typename Function, typename OtherAllocator>
0598 void post(Function&& f, const OtherAllocator& a) const;
0599
0600
0601
0602
0603
0604
0605
0606
0607
0608
0609
0610
0611
0612
0613
0614
0615
0616
0617 template <typename Function, typename OtherAllocator>
0618 void defer(Function&& f, const OtherAllocator& a) const;
0619 #endif
0620
0621 private:
0622 friend class thread_pool;
0623 template <typename, unsigned int> friend class basic_executor_type;
0624
0625
0626 explicit basic_executor_type(thread_pool& p) noexcept
0627 : pool_(&p),
0628 allocator_(),
0629 bits_(0)
0630 {
0631 if (Bits & outstanding_work_tracked)
0632 pool_->scheduler_.work_started();
0633 }
0634
0635
0636 basic_executor_type(thread_pool* p,
0637 const Allocator& a, unsigned int bits) noexcept
0638 : pool_(p),
0639 allocator_(a),
0640 bits_(bits)
0641 {
0642 if (Bits & outstanding_work_tracked)
0643 if (pool_)
0644 pool_->scheduler_.work_started();
0645 }
0646
0647
0648 template <typename Function>
0649 void do_execute(Function&& f, false_type) const;
0650
0651
0652 template <typename Function>
0653 void do_execute(Function&& f, true_type) const;
0654
0655
0656 thread_pool* pool_;
0657
0658
0659 Allocator allocator_;
0660
0661
0662 unsigned int bits_;
0663 };
0664
0665 #if !defined(GENERATING_DOCUMENTATION)
0666
0667 namespace traits {
0668
0669 #if !defined(BOOST_ASIO_HAS_DEDUCED_EQUALITY_COMPARABLE_TRAIT)
0670
0671 template <typename Allocator, unsigned int Bits>
0672 struct equality_comparable<
0673 boost::asio::thread_pool::basic_executor_type<Allocator, Bits>
0674 >
0675 {
0676 static constexpr bool is_valid = true;
0677 static constexpr bool is_noexcept = true;
0678 };
0679
0680 #endif
0681
0682 #if !defined(BOOST_ASIO_HAS_DEDUCED_EXECUTE_MEMBER_TRAIT)
0683
0684 template <typename Allocator, unsigned int Bits, typename Function>
0685 struct execute_member<
0686 boost::asio::thread_pool::basic_executor_type<Allocator, Bits>,
0687 Function
0688 >
0689 {
0690 static constexpr bool is_valid = true;
0691 static constexpr bool is_noexcept = false;
0692 typedef void result_type;
0693 };
0694
0695 #endif
0696
0697 #if !defined(BOOST_ASIO_HAS_DEDUCED_REQUIRE_MEMBER_TRAIT)
0698
0699 template <typename Allocator, unsigned int Bits>
0700 struct require_member<
0701 boost::asio::thread_pool::basic_executor_type<Allocator, Bits>,
0702 boost::asio::execution::blocking_t::possibly_t
0703 > : boost::asio::detail::thread_pool_bits
0704 {
0705 static constexpr bool is_valid = true;
0706 static constexpr bool is_noexcept = true;
0707 typedef boost::asio::thread_pool::basic_executor_type<
0708 Allocator, Bits & ~blocking_mask> result_type;
0709 };
0710
0711 template <typename Allocator, unsigned int Bits>
0712 struct require_member<
0713 boost::asio::thread_pool::basic_executor_type<Allocator, Bits>,
0714 boost::asio::execution::blocking_t::always_t
0715 > : boost::asio::detail::thread_pool_bits
0716 {
0717 static constexpr bool is_valid = true;
0718 static constexpr bool is_noexcept = false;
0719 typedef boost::asio::thread_pool::basic_executor_type<Allocator,
0720 (Bits & ~blocking_mask) | blocking_always> result_type;
0721 };
0722
0723 template <typename Allocator, unsigned int Bits>
0724 struct require_member<
0725 boost::asio::thread_pool::basic_executor_type<Allocator, Bits>,
0726 boost::asio::execution::blocking_t::never_t
0727 > : boost::asio::detail::thread_pool_bits
0728 {
0729 static constexpr bool is_valid = true;
0730 static constexpr bool is_noexcept = false;
0731 typedef boost::asio::thread_pool::basic_executor_type<
0732 Allocator, Bits & ~blocking_mask> result_type;
0733 };
0734
0735 template <typename Allocator, unsigned int Bits>
0736 struct require_member<
0737 boost::asio::thread_pool::basic_executor_type<Allocator, Bits>,
0738 boost::asio::execution::relationship_t::fork_t
0739 >
0740 {
0741 static constexpr bool is_valid = true;
0742 static constexpr bool is_noexcept = false;
0743 typedef boost::asio::thread_pool::basic_executor_type<
0744 Allocator, Bits> result_type;
0745 };
0746
0747 template <typename Allocator, unsigned int Bits>
0748 struct require_member<
0749 boost::asio::thread_pool::basic_executor_type<Allocator, Bits>,
0750 boost::asio::execution::relationship_t::continuation_t
0751 >
0752 {
0753 static constexpr bool is_valid = true;
0754 static constexpr bool is_noexcept = false;
0755 typedef boost::asio::thread_pool::basic_executor_type<
0756 Allocator, Bits> result_type;
0757 };
0758
0759 template <typename Allocator, unsigned int Bits>
0760 struct require_member<
0761 boost::asio::thread_pool::basic_executor_type<Allocator, Bits>,
0762 boost::asio::execution::outstanding_work_t::tracked_t
0763 > : boost::asio::detail::thread_pool_bits
0764 {
0765 static constexpr bool is_valid = true;
0766 static constexpr bool is_noexcept = false;
0767 typedef boost::asio::thread_pool::basic_executor_type<
0768 Allocator, Bits | outstanding_work_tracked> result_type;
0769 };
0770
0771 template <typename Allocator, unsigned int Bits>
0772 struct require_member<
0773 boost::asio::thread_pool::basic_executor_type<Allocator, Bits>,
0774 boost::asio::execution::outstanding_work_t::untracked_t
0775 > : boost::asio::detail::thread_pool_bits
0776 {
0777 static constexpr bool is_valid = true;
0778 static constexpr bool is_noexcept = false;
0779 typedef boost::asio::thread_pool::basic_executor_type<
0780 Allocator, Bits & ~outstanding_work_tracked> result_type;
0781 };
0782
0783 template <typename Allocator, unsigned int Bits>
0784 struct require_member<
0785 boost::asio::thread_pool::basic_executor_type<Allocator, Bits>,
0786 boost::asio::execution::allocator_t<void>
0787 >
0788 {
0789 static constexpr bool is_valid = true;
0790 static constexpr bool is_noexcept = false;
0791 typedef boost::asio::thread_pool::basic_executor_type<
0792 std::allocator<void>, Bits> result_type;
0793 };
0794
0795 template <unsigned int Bits,
0796 typename Allocator, typename OtherAllocator>
0797 struct require_member<
0798 boost::asio::thread_pool::basic_executor_type<Allocator, Bits>,
0799 boost::asio::execution::allocator_t<OtherAllocator>
0800 >
0801 {
0802 static constexpr bool is_valid = true;
0803 static constexpr bool is_noexcept = false;
0804 typedef boost::asio::thread_pool::basic_executor_type<
0805 OtherAllocator, Bits> result_type;
0806 };
0807
0808 #endif
0809
0810 #if !defined(BOOST_ASIO_HAS_DEDUCED_QUERY_STATIC_CONSTEXPR_MEMBER_TRAIT)
0811
0812 template <typename Allocator, unsigned int Bits, typename Property>
0813 struct query_static_constexpr_member<
0814 boost::asio::thread_pool::basic_executor_type<Allocator, Bits>,
0815 Property,
0816 typename boost::asio::enable_if<
0817 boost::asio::is_convertible<
0818 Property,
0819 boost::asio::execution::outstanding_work_t
0820 >::value
0821 >::type
0822 > : boost::asio::detail::thread_pool_bits
0823 {
0824 static constexpr bool is_valid = true;
0825 static constexpr bool is_noexcept = true;
0826 typedef boost::asio::execution::outstanding_work_t result_type;
0827
0828 static constexpr result_type value() noexcept
0829 {
0830 return (Bits & outstanding_work_tracked)
0831 ? execution::outstanding_work_t(execution::outstanding_work.tracked)
0832 : execution::outstanding_work_t(execution::outstanding_work.untracked);
0833 }
0834 };
0835
0836 template <typename Allocator, unsigned int Bits, typename Property>
0837 struct query_static_constexpr_member<
0838 boost::asio::thread_pool::basic_executor_type<Allocator, Bits>,
0839 Property,
0840 typename boost::asio::enable_if<
0841 boost::asio::is_convertible<
0842 Property,
0843 boost::asio::execution::mapping_t
0844 >::value
0845 >::type
0846 >
0847 {
0848 static constexpr bool is_valid = true;
0849 static constexpr bool is_noexcept = true;
0850 typedef boost::asio::execution::mapping_t::thread_t result_type;
0851
0852 static constexpr result_type value() noexcept
0853 {
0854 return result_type();
0855 }
0856 };
0857
0858 #endif
0859
0860 #if !defined(BOOST_ASIO_HAS_DEDUCED_QUERY_MEMBER_TRAIT)
0861
0862 template <typename Allocator, unsigned int Bits, typename Property>
0863 struct query_member<
0864 boost::asio::thread_pool::basic_executor_type<Allocator, Bits>,
0865 Property,
0866 typename boost::asio::enable_if<
0867 boost::asio::is_convertible<
0868 Property,
0869 boost::asio::execution::blocking_t
0870 >::value
0871 >::type
0872 >
0873 {
0874 static constexpr bool is_valid = true;
0875 static constexpr bool is_noexcept = true;
0876 typedef boost::asio::execution::blocking_t result_type;
0877 };
0878
0879 template <typename Allocator, unsigned int Bits, typename Property>
0880 struct query_member<
0881 boost::asio::thread_pool::basic_executor_type<Allocator, Bits>,
0882 Property,
0883 typename boost::asio::enable_if<
0884 boost::asio::is_convertible<
0885 Property,
0886 boost::asio::execution::relationship_t
0887 >::value
0888 >::type
0889 >
0890 {
0891 static constexpr bool is_valid = true;
0892 static constexpr bool is_noexcept = true;
0893 typedef boost::asio::execution::relationship_t result_type;
0894 };
0895
0896 template <typename Allocator, unsigned int Bits>
0897 struct query_member<
0898 boost::asio::thread_pool::basic_executor_type<Allocator, Bits>,
0899 boost::asio::execution::occupancy_t
0900 >
0901 {
0902 static constexpr bool is_valid = true;
0903 static constexpr bool is_noexcept = true;
0904 typedef std::size_t result_type;
0905 };
0906
0907 template <typename Allocator, unsigned int Bits>
0908 struct query_member<
0909 boost::asio::thread_pool::basic_executor_type<Allocator, Bits>,
0910 boost::asio::execution::context_t
0911 >
0912 {
0913 static constexpr bool is_valid = true;
0914 static constexpr bool is_noexcept = true;
0915 typedef boost::asio::thread_pool& result_type;
0916 };
0917
0918 template <typename Allocator, unsigned int Bits>
0919 struct query_member<
0920 boost::asio::thread_pool::basic_executor_type<Allocator, Bits>,
0921 boost::asio::execution::allocator_t<void>
0922 >
0923 {
0924 static constexpr bool is_valid = true;
0925 static constexpr bool is_noexcept = true;
0926 typedef Allocator result_type;
0927 };
0928
0929 template <typename Allocator, unsigned int Bits, typename OtherAllocator>
0930 struct query_member<
0931 boost::asio::thread_pool::basic_executor_type<Allocator, Bits>,
0932 boost::asio::execution::allocator_t<OtherAllocator>
0933 >
0934 {
0935 static constexpr bool is_valid = true;
0936 static constexpr bool is_noexcept = true;
0937 typedef Allocator result_type;
0938 };
0939
0940 #endif
0941
0942 }
0943
0944 namespace execution {
0945
0946 template <>
0947 struct is_executor<thread_pool> : false_type
0948 {
0949 };
0950
0951 }
0952
0953 #endif
0954
0955 }
0956 }
0957
0958 #include <boost/asio/detail/pop_options.hpp>
0959
0960 #include <boost/asio/impl/thread_pool.hpp>
0961 #if defined(BOOST_ASIO_HEADER_ONLY)
0962 # include <boost/asio/impl/thread_pool.ipp>
0963 #endif
0964
0965 #endif