annotate DEPENDENCIES/generic/include/boost/thread/win32/condition_variable.hpp @ 125:34e428693f5d vext

Vext -> Repoint
author Chris Cannam
date Thu, 14 Jun 2018 11:15:39 +0100
parents c530137014c0
children
rev   line source
Chris@16 1 #ifndef BOOST_THREAD_CONDITION_VARIABLE_WIN32_HPP
Chris@16 2 #define BOOST_THREAD_CONDITION_VARIABLE_WIN32_HPP
Chris@16 3 // Distributed under the Boost Software License, Version 1.0. (See
Chris@16 4 // accompanying file LICENSE_1_0.txt or copy at
Chris@16 5 // http://www.boost.org/LICENSE_1_0.txt)
Chris@16 6 // (C) Copyright 2007-8 Anthony Williams
Chris@16 7 // (C) Copyright 2011-2012 Vicente J. Botet Escriba
Chris@16 8
Chris@16 9 #include <boost/thread/win32/thread_primitives.hpp>
Chris@16 10 #include <boost/thread/win32/thread_data.hpp>
Chris@16 11 #include <boost/thread/win32/thread_data.hpp>
Chris@16 12 #include <boost/thread/win32/interlocked_read.hpp>
Chris@16 13 #include <boost/thread/cv_status.hpp>
Chris@16 14 #if defined BOOST_THREAD_USES_DATETIME
Chris@16 15 #include <boost/thread/xtime.hpp>
Chris@16 16 #endif
Chris@16 17 #include <boost/thread/mutex.hpp>
Chris@16 18 #include <boost/thread/thread_time.hpp>
Chris@16 19 #include <boost/thread/lock_guard.hpp>
Chris@16 20 #include <boost/thread/lock_types.hpp>
Chris@16 21
Chris@16 22 #include <boost/assert.hpp>
Chris@16 23 #include <boost/intrusive_ptr.hpp>
Chris@16 24
Chris@16 25 #ifdef BOOST_THREAD_USES_CHRONO
Chris@16 26 #include <boost/chrono/system_clocks.hpp>
Chris@16 27 #include <boost/chrono/ceil.hpp>
Chris@16 28 #endif
Chris@16 29
Chris@16 30 #include <limits.h>
Chris@16 31 #include <algorithm>
Chris@16 32 #include <vector>
Chris@16 33
Chris@16 34 #include <boost/config/abi_prefix.hpp>
Chris@16 35
Chris@16 36 namespace boost
Chris@16 37 {
Chris@16 38 namespace detail
Chris@16 39 {
Chris@16 40 class basic_cv_list_entry;
Chris@16 41 void intrusive_ptr_add_ref(basic_cv_list_entry * p);
Chris@16 42 void intrusive_ptr_release(basic_cv_list_entry * p);
Chris@16 43
Chris@16 44 class basic_cv_list_entry
Chris@16 45 {
Chris@16 46 private:
Chris@16 47 detail::win32::handle_manager semaphore;
Chris@16 48 detail::win32::handle_manager wake_sem;
Chris@16 49 long waiters;
Chris@16 50 bool notified;
Chris@16 51 long references;
Chris@16 52
Chris@16 53 public:
Chris@16 54 BOOST_THREAD_NO_COPYABLE(basic_cv_list_entry)
Chris@16 55 explicit basic_cv_list_entry(detail::win32::handle_manager const& wake_sem_):
Chris@16 56 semaphore(detail::win32::create_anonymous_semaphore(0,LONG_MAX)),
Chris@16 57 wake_sem(wake_sem_.duplicate()),
Chris@16 58 waiters(1),notified(false),references(0)
Chris@16 59 {}
Chris@16 60
Chris@16 61 static bool no_waiters(boost::intrusive_ptr<basic_cv_list_entry> const& entry)
Chris@16 62 {
Chris@16 63 return !detail::interlocked_read_acquire(&entry->waiters);
Chris@16 64 }
Chris@16 65
Chris@16 66 void add_waiter()
Chris@16 67 {
Chris@16 68 BOOST_INTERLOCKED_INCREMENT(&waiters);
Chris@16 69 }
Chris@16 70
Chris@16 71 void remove_waiter()
Chris@16 72 {
Chris@16 73 BOOST_INTERLOCKED_DECREMENT(&waiters);
Chris@16 74 }
Chris@16 75
Chris@16 76 void release(unsigned count_to_release)
Chris@16 77 {
Chris@16 78 notified=true;
Chris@16 79 detail::win32::ReleaseSemaphore(semaphore,count_to_release,0);
Chris@16 80 }
Chris@16 81
Chris@16 82 void release_waiters()
Chris@16 83 {
Chris@16 84 release(detail::interlocked_read_acquire(&waiters));
Chris@16 85 }
Chris@16 86
Chris@16 87 bool is_notified() const
Chris@16 88 {
Chris@16 89 return notified;
Chris@16 90 }
Chris@16 91
Chris@16 92 bool wait(timeout abs_time)
Chris@16 93 {
Chris@16 94 return this_thread::interruptible_wait(semaphore,abs_time);
Chris@16 95 }
Chris@16 96
Chris@16 97 bool woken()
Chris@16 98 {
Chris@101 99 unsigned long const woken_result=detail::win32::WaitForSingleObjectEx(wake_sem,0,0);
Chris@16 100 BOOST_ASSERT((woken_result==detail::win32::timeout) || (woken_result==0));
Chris@16 101 return woken_result==0;
Chris@16 102 }
Chris@16 103
Chris@16 104 friend void intrusive_ptr_add_ref(basic_cv_list_entry * p);
Chris@16 105 friend void intrusive_ptr_release(basic_cv_list_entry * p);
Chris@16 106 };
Chris@16 107
Chris@16 108 inline void intrusive_ptr_add_ref(basic_cv_list_entry * p)
Chris@16 109 {
Chris@16 110 BOOST_INTERLOCKED_INCREMENT(&p->references);
Chris@16 111 }
Chris@16 112
Chris@16 113 inline void intrusive_ptr_release(basic_cv_list_entry * p)
Chris@16 114 {
Chris@16 115 if(!BOOST_INTERLOCKED_DECREMENT(&p->references))
Chris@16 116 {
Chris@16 117 delete p;
Chris@16 118 }
Chris@16 119 }
Chris@16 120
Chris@16 121 class basic_condition_variable
Chris@16 122 {
Chris@16 123 boost::mutex internal_mutex;
Chris@16 124 long total_count;
Chris@16 125 unsigned active_generation_count;
Chris@16 126
Chris@16 127 typedef basic_cv_list_entry list_entry;
Chris@16 128
Chris@16 129 typedef boost::intrusive_ptr<list_entry> entry_ptr;
Chris@16 130 typedef std::vector<entry_ptr> generation_list;
Chris@16 131
Chris@16 132 generation_list generations;
Chris@16 133 detail::win32::handle_manager wake_sem;
Chris@16 134
Chris@16 135 void wake_waiters(long count_to_wake)
Chris@16 136 {
Chris@16 137 detail::interlocked_write_release(&total_count,total_count-count_to_wake);
Chris@16 138 detail::win32::ReleaseSemaphore(wake_sem,count_to_wake,0);
Chris@16 139 }
Chris@16 140
Chris@16 141 template<typename lock_type>
Chris@16 142 struct relocker
Chris@16 143 {
Chris@16 144 BOOST_THREAD_NO_COPYABLE(relocker)
Chris@16 145 lock_type& lock;
Chris@16 146 bool unlocked;
Chris@16 147
Chris@16 148 relocker(lock_type& lock_):
Chris@16 149 lock(lock_),unlocked(false)
Chris@16 150 {}
Chris@16 151 void unlock()
Chris@16 152 {
Chris@16 153 lock.unlock();
Chris@16 154 unlocked=true;
Chris@16 155 }
Chris@16 156 ~relocker()
Chris@16 157 {
Chris@16 158 if(unlocked)
Chris@16 159 {
Chris@16 160 lock.lock();
Chris@16 161 }
Chris@16 162
Chris@16 163 }
Chris@16 164 };
Chris@16 165
Chris@16 166
Chris@16 167 entry_ptr get_wait_entry()
Chris@16 168 {
Chris@16 169 boost::lock_guard<boost::mutex> internal_lock(internal_mutex);
Chris@16 170
Chris@16 171 if(!wake_sem)
Chris@16 172 {
Chris@16 173 wake_sem=detail::win32::create_anonymous_semaphore(0,LONG_MAX);
Chris@16 174 BOOST_ASSERT(wake_sem);
Chris@16 175 }
Chris@16 176
Chris@16 177 detail::interlocked_write_release(&total_count,total_count+1);
Chris@16 178 if(generations.empty() || generations.back()->is_notified())
Chris@16 179 {
Chris@16 180 entry_ptr new_entry(new list_entry(wake_sem));
Chris@16 181 generations.push_back(new_entry);
Chris@16 182 return new_entry;
Chris@16 183 }
Chris@16 184 else
Chris@16 185 {
Chris@16 186 generations.back()->add_waiter();
Chris@16 187 return generations.back();
Chris@16 188 }
Chris@16 189 }
Chris@16 190
Chris@16 191 struct entry_manager
Chris@16 192 {
Chris@16 193 entry_ptr const entry;
Chris@16 194 boost::mutex& internal_mutex;
Chris@16 195
Chris@16 196 BOOST_THREAD_NO_COPYABLE(entry_manager)
Chris@16 197 entry_manager(entry_ptr const& entry_, boost::mutex& mutex_):
Chris@16 198 entry(entry_), internal_mutex(mutex_)
Chris@16 199 {}
Chris@16 200
Chris@16 201 ~entry_manager()
Chris@16 202 {
Chris@16 203 boost::lock_guard<boost::mutex> internal_lock(internal_mutex);
Chris@16 204 entry->remove_waiter();
Chris@16 205 }
Chris@16 206
Chris@16 207 list_entry* operator->()
Chris@16 208 {
Chris@16 209 return entry.get();
Chris@16 210 }
Chris@16 211 };
Chris@16 212
Chris@16 213
Chris@16 214 protected:
Chris@16 215 template<typename lock_type>
Chris@16 216 bool do_wait(lock_type& lock,timeout abs_time)
Chris@16 217 {
Chris@16 218 relocker<lock_type> locker(lock);
Chris@16 219
Chris@16 220 entry_manager entry(get_wait_entry(), internal_mutex);
Chris@16 221
Chris@16 222 locker.unlock();
Chris@16 223
Chris@16 224 bool woken=false;
Chris@16 225 while(!woken)
Chris@16 226 {
Chris@16 227 if(!entry->wait(abs_time))
Chris@16 228 {
Chris@16 229 return false;
Chris@16 230 }
Chris@16 231
Chris@16 232 woken=entry->woken();
Chris@16 233 }
Chris@16 234 return woken;
Chris@16 235 }
Chris@16 236
Chris@16 237 template<typename lock_type,typename predicate_type>
Chris@16 238 bool do_wait(lock_type& m,timeout const& abs_time,predicate_type pred)
Chris@16 239 {
Chris@16 240 while (!pred())
Chris@16 241 {
Chris@16 242 if(!do_wait(m, abs_time))
Chris@16 243 return pred();
Chris@16 244 }
Chris@16 245 return true;
Chris@16 246 }
Chris@16 247
Chris@16 248 basic_condition_variable(const basic_condition_variable& other);
Chris@16 249 basic_condition_variable& operator=(const basic_condition_variable& other);
Chris@16 250
Chris@16 251 public:
Chris@16 252 basic_condition_variable():
Chris@16 253 total_count(0),active_generation_count(0),wake_sem(0)
Chris@16 254 {}
Chris@16 255
Chris@16 256 ~basic_condition_variable()
Chris@16 257 {}
Chris@16 258
Chris@16 259 void notify_one() BOOST_NOEXCEPT
Chris@16 260 {
Chris@16 261 if(detail::interlocked_read_acquire(&total_count))
Chris@16 262 {
Chris@16 263 boost::lock_guard<boost::mutex> internal_lock(internal_mutex);
Chris@16 264 if(!total_count)
Chris@16 265 {
Chris@16 266 return;
Chris@16 267 }
Chris@16 268 wake_waiters(1);
Chris@16 269
Chris@16 270 for(generation_list::iterator it=generations.begin(),
Chris@16 271 end=generations.end();
Chris@16 272 it!=end;++it)
Chris@16 273 {
Chris@16 274 (*it)->release(1);
Chris@16 275 }
Chris@16 276 generations.erase(std::remove_if(generations.begin(),generations.end(),&basic_cv_list_entry::no_waiters),generations.end());
Chris@16 277 }
Chris@16 278 }
Chris@16 279
Chris@16 280 void notify_all() BOOST_NOEXCEPT
Chris@16 281 {
Chris@16 282 if(detail::interlocked_read_acquire(&total_count))
Chris@16 283 {
Chris@16 284 boost::lock_guard<boost::mutex> internal_lock(internal_mutex);
Chris@16 285 if(!total_count)
Chris@16 286 {
Chris@16 287 return;
Chris@16 288 }
Chris@16 289 wake_waiters(total_count);
Chris@16 290 for(generation_list::iterator it=generations.begin(),
Chris@16 291 end=generations.end();
Chris@16 292 it!=end;++it)
Chris@16 293 {
Chris@16 294 (*it)->release_waiters();
Chris@16 295 }
Chris@16 296 generations.clear();
Chris@16 297 wake_sem=detail::win32::handle(0);
Chris@16 298 }
Chris@16 299 }
Chris@16 300
Chris@16 301 };
Chris@16 302 }
Chris@16 303
Chris@16 304 class condition_variable:
Chris@16 305 private detail::basic_condition_variable
Chris@16 306 {
Chris@16 307 public:
Chris@16 308 BOOST_THREAD_NO_COPYABLE(condition_variable)
Chris@16 309 condition_variable()
Chris@16 310 {}
Chris@16 311
Chris@16 312 using detail::basic_condition_variable::notify_one;
Chris@16 313 using detail::basic_condition_variable::notify_all;
Chris@16 314
Chris@16 315 void wait(unique_lock<mutex>& m)
Chris@16 316 {
Chris@16 317 do_wait(m,detail::timeout::sentinel());
Chris@16 318 }
Chris@16 319
Chris@16 320 template<typename predicate_type>
Chris@16 321 void wait(unique_lock<mutex>& m,predicate_type pred)
Chris@16 322 {
Chris@16 323 while(!pred()) wait(m);
Chris@16 324 }
Chris@16 325
Chris@16 326
Chris@16 327 #if defined BOOST_THREAD_USES_DATETIME
Chris@16 328 bool timed_wait(unique_lock<mutex>& m,boost::system_time const& abs_time)
Chris@16 329 {
Chris@16 330 return do_wait(m,abs_time);
Chris@16 331 }
Chris@16 332
Chris@16 333 bool timed_wait(unique_lock<mutex>& m,boost::xtime const& abs_time)
Chris@16 334 {
Chris@16 335 return do_wait(m,system_time(abs_time));
Chris@16 336 }
Chris@16 337 template<typename duration_type>
Chris@16 338 bool timed_wait(unique_lock<mutex>& m,duration_type const& wait_duration)
Chris@16 339 {
Chris@101 340 if (wait_duration.is_pos_infinity())
Chris@101 341 {
Chris@101 342 wait(m); // or do_wait(m,detail::timeout::sentinel());
Chris@101 343 return true;
Chris@101 344 }
Chris@101 345 if (wait_duration.is_special())
Chris@101 346 {
Chris@101 347 return true;
Chris@101 348 }
Chris@101 349 return do_wait(m,wait_duration.total_milliseconds());
Chris@16 350 }
Chris@16 351
Chris@16 352 template<typename predicate_type>
Chris@16 353 bool timed_wait(unique_lock<mutex>& m,boost::system_time const& abs_time,predicate_type pred)
Chris@16 354 {
Chris@16 355 return do_wait(m,abs_time,pred);
Chris@16 356 }
Chris@16 357 template<typename predicate_type>
Chris@16 358 bool timed_wait(unique_lock<mutex>& m,boost::xtime const& abs_time,predicate_type pred)
Chris@16 359 {
Chris@16 360 return do_wait(m,system_time(abs_time),pred);
Chris@16 361 }
Chris@16 362 template<typename duration_type,typename predicate_type>
Chris@16 363 bool timed_wait(unique_lock<mutex>& m,duration_type const& wait_duration,predicate_type pred)
Chris@16 364 {
Chris@16 365 return do_wait(m,wait_duration.total_milliseconds(),pred);
Chris@16 366 }
Chris@16 367 #endif
Chris@16 368 #ifdef BOOST_THREAD_USES_CHRONO
Chris@16 369
Chris@16 370 template <class Clock, class Duration>
Chris@16 371 cv_status
Chris@16 372 wait_until(
Chris@16 373 unique_lock<mutex>& lock,
Chris@16 374 const chrono::time_point<Clock, Duration>& t)
Chris@16 375 {
Chris@16 376 using namespace chrono;
Chris@16 377 chrono::time_point<Clock, Duration> now = Clock::now();
Chris@16 378 if (t<=now) {
Chris@16 379 return cv_status::timeout;
Chris@16 380 }
Chris@16 381 do_wait(lock, ceil<milliseconds>(t-now).count());
Chris@16 382 return Clock::now() < t ? cv_status::no_timeout :
Chris@16 383 cv_status::timeout;
Chris@16 384 }
Chris@16 385
Chris@16 386 template <class Rep, class Period>
Chris@16 387 cv_status
Chris@16 388 wait_for(
Chris@16 389 unique_lock<mutex>& lock,
Chris@16 390 const chrono::duration<Rep, Period>& d)
Chris@16 391 {
Chris@16 392 using namespace chrono;
Chris@16 393 if (d<=chrono::duration<Rep, Period>::zero()) {
Chris@16 394 return cv_status::timeout;
Chris@16 395 }
Chris@16 396
Chris@16 397 steady_clock::time_point c_now = steady_clock::now();
Chris@16 398 do_wait(lock, ceil<milliseconds>(d).count());
Chris@16 399 return steady_clock::now() - c_now < d ? cv_status::no_timeout :
Chris@16 400 cv_status::timeout;
Chris@16 401 }
Chris@16 402
Chris@16 403 template <class Clock, class Duration, class Predicate>
Chris@16 404 bool
Chris@16 405 wait_until(
Chris@16 406 unique_lock<mutex>& lock,
Chris@16 407 const chrono::time_point<Clock, Duration>& t,
Chris@16 408 Predicate pred)
Chris@16 409 {
Chris@16 410 while (!pred())
Chris@16 411 {
Chris@16 412 if (wait_until(lock, t) == cv_status::timeout)
Chris@16 413 return pred();
Chris@16 414 }
Chris@16 415 return true;
Chris@16 416 }
Chris@16 417 template <class Rep, class Period, class Predicate>
Chris@16 418 bool
Chris@16 419 wait_for(
Chris@16 420 unique_lock<mutex>& lock,
Chris@16 421 const chrono::duration<Rep, Period>& d,
Chris@16 422 Predicate pred)
Chris@16 423 {
Chris@16 424 return wait_until(lock, chrono::steady_clock::now() + d, boost::move(pred));
Chris@16 425 }
Chris@16 426 #endif
Chris@16 427 };
Chris@16 428
Chris@16 429 class condition_variable_any:
Chris@16 430 private detail::basic_condition_variable
Chris@16 431 {
Chris@16 432 public:
Chris@16 433 BOOST_THREAD_NO_COPYABLE(condition_variable_any)
Chris@16 434 condition_variable_any()
Chris@16 435 {}
Chris@16 436
Chris@16 437 using detail::basic_condition_variable::notify_one;
Chris@16 438 using detail::basic_condition_variable::notify_all;
Chris@16 439
Chris@16 440 template<typename lock_type>
Chris@16 441 void wait(lock_type& m)
Chris@16 442 {
Chris@16 443 do_wait(m,detail::timeout::sentinel());
Chris@16 444 }
Chris@16 445
Chris@16 446 template<typename lock_type,typename predicate_type>
Chris@16 447 void wait(lock_type& m,predicate_type pred)
Chris@16 448 {
Chris@16 449 while(!pred()) wait(m);
Chris@16 450 }
Chris@16 451
Chris@16 452 #if defined BOOST_THREAD_USES_DATETIME
Chris@16 453 template<typename lock_type>
Chris@16 454 bool timed_wait(lock_type& m,boost::system_time const& abs_time)
Chris@16 455 {
Chris@16 456 return do_wait(m,abs_time);
Chris@16 457 }
Chris@16 458
Chris@16 459 template<typename lock_type>
Chris@16 460 bool timed_wait(lock_type& m,boost::xtime const& abs_time)
Chris@16 461 {
Chris@16 462 return do_wait(m,system_time(abs_time));
Chris@16 463 }
Chris@16 464
Chris@16 465 template<typename lock_type,typename duration_type>
Chris@16 466 bool timed_wait(lock_type& m,duration_type const& wait_duration)
Chris@16 467 {
Chris@16 468 return do_wait(m,wait_duration.total_milliseconds());
Chris@16 469 }
Chris@16 470
Chris@16 471 template<typename lock_type,typename predicate_type>
Chris@16 472 bool timed_wait(lock_type& m,boost::system_time const& abs_time,predicate_type pred)
Chris@16 473 {
Chris@16 474 return do_wait(m,abs_time,pred);
Chris@16 475 }
Chris@16 476
Chris@16 477 template<typename lock_type,typename predicate_type>
Chris@16 478 bool timed_wait(lock_type& m,boost::xtime const& abs_time,predicate_type pred)
Chris@16 479 {
Chris@16 480 return do_wait(m,system_time(abs_time),pred);
Chris@16 481 }
Chris@16 482
Chris@16 483 template<typename lock_type,typename duration_type,typename predicate_type>
Chris@16 484 bool timed_wait(lock_type& m,duration_type const& wait_duration,predicate_type pred)
Chris@16 485 {
Chris@16 486 return do_wait(m,wait_duration.total_milliseconds(),pred);
Chris@16 487 }
Chris@16 488 #endif
Chris@16 489 #ifdef BOOST_THREAD_USES_CHRONO
Chris@16 490
Chris@16 491 template <class lock_type, class Clock, class Duration>
Chris@16 492 cv_status
Chris@16 493 wait_until(
Chris@16 494 lock_type& lock,
Chris@16 495 const chrono::time_point<Clock, Duration>& t)
Chris@16 496 {
Chris@16 497 using namespace chrono;
Chris@16 498 chrono::time_point<Clock, Duration> now = Clock::now();
Chris@16 499 if (t<=now) {
Chris@16 500 return cv_status::timeout;
Chris@16 501 }
Chris@16 502 do_wait(lock, ceil<milliseconds>(t-now).count());
Chris@16 503 return Clock::now() < t ? cv_status::no_timeout :
Chris@16 504 cv_status::timeout;
Chris@16 505 }
Chris@16 506
Chris@16 507 template <class lock_type, class Rep, class Period>
Chris@16 508 cv_status
Chris@16 509 wait_for(
Chris@16 510 lock_type& lock,
Chris@16 511 const chrono::duration<Rep, Period>& d)
Chris@16 512 {
Chris@16 513 using namespace chrono;
Chris@16 514 if (d<=chrono::duration<Rep, Period>::zero()) {
Chris@16 515 return cv_status::timeout;
Chris@16 516 }
Chris@16 517 steady_clock::time_point c_now = steady_clock::now();
Chris@16 518 do_wait(lock, ceil<milliseconds>(d).count());
Chris@16 519 return steady_clock::now() - c_now < d ? cv_status::no_timeout :
Chris@16 520 cv_status::timeout;
Chris@16 521 }
Chris@16 522
Chris@16 523 template <class lock_type, class Clock, class Duration, class Predicate>
Chris@16 524 bool
Chris@16 525 wait_until(
Chris@16 526 lock_type& lock,
Chris@16 527 const chrono::time_point<Clock, Duration>& t,
Chris@16 528 Predicate pred)
Chris@16 529 {
Chris@16 530 while (!pred())
Chris@16 531 {
Chris@16 532 if (wait_until(lock, t) == cv_status::timeout)
Chris@16 533 return pred();
Chris@16 534 }
Chris@16 535 return true;
Chris@16 536 }
Chris@16 537
Chris@16 538 template <class lock_type, class Rep, class Period, class Predicate>
Chris@16 539 bool
Chris@16 540 wait_for(
Chris@16 541 lock_type& lock,
Chris@16 542 const chrono::duration<Rep, Period>& d,
Chris@16 543 Predicate pred)
Chris@16 544 {
Chris@16 545 return wait_until(lock, chrono::steady_clock::now() + d, boost::move(pred));
Chris@16 546 }
Chris@16 547 #endif
Chris@16 548 };
Chris@16 549
Chris@16 550 BOOST_THREAD_DECL void notify_all_at_thread_exit(condition_variable& cond, unique_lock<mutex> lk);
Chris@16 551 }
Chris@16 552
Chris@16 553 #include <boost/config/abi_suffix.hpp>
Chris@16 554
Chris@16 555 #endif