Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(210)

Side by Side Diff: webrtc/base/sigslot.h

Issue 2846593005: Relanding #2: Fixing crash that can occur if signal is modified while firing. (Closed)
Patch Set: Fixing issue with disconnect_all, adding test, simplifying code a bit Created 3 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « webrtc/base/asyncinvoker.cc ('k') | webrtc/base/sigslot_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // sigslot.h: Signal/Slot classes 1 // sigslot.h: Signal/Slot classes
2 // 2 //
3 // Written by Sarah Thompson (sarah@telergy.com) 2002. 3 // Written by Sarah Thompson (sarah@telergy.com) 2002.
4 // 4 //
5 // License: Public domain. You are free to use this code however you like, with the proviso that 5 // License: Public domain. You are free to use this code however you like, with the proviso that
6 // the author takes on no responsibility or liability for any use. 6 // the author takes on no responsibility or liability for any use.
7 // 7 //
8 // QUICK DOCUMENTATION 8 // QUICK DOCUMENTATION
9 // 9 //
10 // (see also the full documentation at http://sigsl ot.sourceforge.net/) 10 // (see also the full documentation at http://sigsl ot.sourceforge.net/)
(...skipping 376 matching lines...) Expand 10 before | Expand all | Expand 10 after
387 (static_cast< DestT* >(self->pdest)->*(pm))(args...); 387 (static_cast< DestT* >(self->pdest)->*(pm))(args...);
388 } 388 }
389 }; 389 };
390 390
391 template<class mt_policy> 391 template<class mt_policy>
392 class _signal_base : public _signal_base_interface, public mt_policy 392 class _signal_base : public _signal_base_interface, public mt_policy
393 { 393 {
394 protected: 394 protected:
395 typedef std::list< _opaque_connection > connections_list; 395 typedef std::list< _opaque_connection > connections_list;
396 396
397 » » _signal_base() : _signal_base_interface(&_signal_base::do_slot_d isconnect, &_signal_base::do_slot_duplicate) 397 » » _signal_base() : _signal_base_interface(&_signal_base::do_slot_d isconnect, &_signal_base::do_slot_duplicate),
398 m_current_iterator(m_connected_slots.end())
398 { 399 {
399 } 400 }
400 401
401 ~_signal_base() 402 ~_signal_base()
402 { 403 {
403 disconnect_all(); 404 disconnect_all();
404 } 405 }
405 406
406 private: 407 private:
407 _signal_base& operator= (_signal_base const& that); 408 _signal_base& operator= (_signal_base const& that);
408 409
409 public: 410 public:
410 » » _signal_base(const _signal_base& o) : _signal_base_interface(&_s ignal_base::do_slot_disconnect, &_signal_base::do_slot_duplicate) { 411 » » _signal_base(const _signal_base& o) : _signal_base_interface(&_s ignal_base::do_slot_disconnect, &_signal_base::do_slot_duplicate),
412 m_current_iterator(m_connected_slots.end()) {
411 lock_block<mt_policy> lock(this); 413 lock_block<mt_policy> lock(this);
412 for (const auto& connection : o.m_connected_slots) 414 for (const auto& connection : o.m_connected_slots)
413 { 415 {
414 connection.getdest()->signal_connect(this); 416 connection.getdest()->signal_connect(this);
415 m_connected_slots.push_back(connection); 417 m_connected_slots.push_back(connection);
416 } 418 }
417 } 419 }
418 420
419 bool is_empty() 421 bool is_empty()
420 { 422 {
421 lock_block<mt_policy> lock(this); 423 lock_block<mt_policy> lock(this);
422 return m_connected_slots.empty(); 424 return m_connected_slots.empty();
423 } 425 }
424 426
425 void disconnect_all() 427 void disconnect_all()
426 { 428 {
427 lock_block<mt_policy> lock(this); 429 lock_block<mt_policy> lock(this);
428 430
429 while(!m_connected_slots.empty()) 431 while(!m_connected_slots.empty())
430 { 432 {
431 has_slots_interface* pdest = m_connected_slots.f ront().getdest(); 433 has_slots_interface* pdest = m_connected_slots.f ront().getdest();
432 m_connected_slots.pop_front(); 434 m_connected_slots.pop_front();
433 pdest->signal_disconnect(static_cast< _signal_ba se_interface* >(this)); 435 pdest->signal_disconnect(static_cast< _signal_ba se_interface* >(this));
434 } 436 }
437 // If disconnect_all is called while the signal is firing, advance the
438 // current slot iterator to the end to avoid an invalidated iterator from
439 // being dereferenced.
440 m_current_iterator = m_connected_slots.end();
435 } 441 }
436 442
437 #if !defined(NDEBUG) 443 #if !defined(NDEBUG)
438 bool connected(has_slots_interface* pclass) 444 bool connected(has_slots_interface* pclass)
439 { 445 {
440 lock_block<mt_policy> lock(this); 446 lock_block<mt_policy> lock(this);
441 connections_list::const_iterator it = m_connected_slots. begin(); 447 connections_list::const_iterator it = m_connected_slots. begin();
442 connections_list::const_iterator itEnd = m_connected_slo ts.end(); 448 connections_list::const_iterator itEnd = m_connected_slo ts.end();
443 while(it != itEnd) 449 while(it != itEnd)
444 { 450 {
445 if (it->getdest() == pclass) 451 if (it->getdest() == pclass)
446 return true; 452 return true;
447 ++it; 453 ++it;
448 } 454 }
449 return false; 455 return false;
450 } 456 }
451 #endif 457 #endif
452 458
453 void disconnect(has_slots_interface* pclass) 459 void disconnect(has_slots_interface* pclass)
454 { 460 {
455 lock_block<mt_policy> lock(this); 461 lock_block<mt_policy> lock(this);
456 connections_list::iterator it = m_connected_slots.begin( ); 462 connections_list::iterator it = m_connected_slots.begin( );
457 connections_list::iterator itEnd = m_connected_slots.end (); 463 connections_list::iterator itEnd = m_connected_slots.end ();
458 464
459 while(it != itEnd) 465 while(it != itEnd)
460 { 466 {
461 if(it->getdest() == pclass) 467 if(it->getdest() == pclass)
462 { 468 {
463 » » » » » m_connected_slots.erase(it); 469 // If we're currently using this iterator because the signal is
470 // firing, advance it to avoid it being invalidated.
471 if (m_current_iterator == it) {
472 m_current_iterator = m_connected_slots.erase(it);
473 } else {
474 m_connected_slots.erase(it);
475 }
464 pclass->signal_disconnect(static_cast< _ signal_base_interface* >(this)); 476 pclass->signal_disconnect(static_cast< _ signal_base_interface* >(this));
465 return; 477 return;
466 } 478 }
467 479
468 ++it; 480 ++it;
469 } 481 }
470 } 482 }
471 483
472 private: 484 private:
473 static void do_slot_disconnect(_signal_base_interface* p, has_sl ots_interface* pslot) 485 static void do_slot_disconnect(_signal_base_interface* p, has_sl ots_interface* pslot)
474 { 486 {
475 _signal_base* const self = static_cast< _signal_base* >( p); 487 _signal_base* const self = static_cast< _signal_base* >( p);
476 lock_block<mt_policy> lock(self); 488 lock_block<mt_policy> lock(self);
477 connections_list::iterator it = self->m_connected_slots. begin(); 489 connections_list::iterator it = self->m_connected_slots. begin();
478 connections_list::iterator itEnd = self->m_connected_slo ts.end(); 490 connections_list::iterator itEnd = self->m_connected_slo ts.end();
479 491
480 while(it != itEnd) 492 while(it != itEnd)
481 { 493 {
482 connections_list::iterator itNext = it; 494 connections_list::iterator itNext = it;
483 ++itNext; 495 ++itNext;
484 496
485 if(it->getdest() == pslot) 497 if(it->getdest() == pslot)
486 { 498 {
487 » » » » » self->m_connected_slots.erase(it); 499 // If we're currently using this iterator because the signal is
488 » » » » } 500 // firing, advance it to avoid it being invalidated.
501 if (self->m_current_iterator == it) {
502 self->m_current_iterator = self->m_connected_slots.erase(it);
503 } else {
504 self->m_connected_slots.erase(it);
505 }
506 }
489 507
490 it = itNext; 508 it = itNext;
491 } 509 }
492 } 510 }
493 511
494 static void do_slot_duplicate(_signal_base_interface* p, const h as_slots_interface* oldtarget, has_slots_interface* newtarget) 512 static void do_slot_duplicate(_signal_base_interface* p, const h as_slots_interface* oldtarget, has_slots_interface* newtarget)
495 { 513 {
496 _signal_base* const self = static_cast< _signal_base* >( p); 514 _signal_base* const self = static_cast< _signal_base* >( p);
497 lock_block<mt_policy> lock(self); 515 lock_block<mt_policy> lock(self);
498 connections_list::iterator it = self->m_connected_slots. begin(); 516 connections_list::iterator it = self->m_connected_slots. begin();
499 connections_list::iterator itEnd = self->m_connected_slo ts.end(); 517 connections_list::iterator itEnd = self->m_connected_slo ts.end();
500 518
501 while(it != itEnd) 519 while(it != itEnd)
502 { 520 {
503 if(it->getdest() == oldtarget) 521 if(it->getdest() == oldtarget)
504 { 522 {
505 self->m_connected_slots.push_back(it->du plicate(newtarget)); 523 self->m_connected_slots.push_back(it->du plicate(newtarget));
506 } 524 }
507 525
508 ++it; 526 ++it;
509 } 527 }
510 } 528 }
511 529
512 protected: 530 protected:
513 connections_list m_connected_slots; 531 connections_list m_connected_slots;
514 » }; 532
533 // Used to handle a slot being disconnected while a signal is
534 // firing (iterating m_connected_slots).
535 connections_list::iterator m_current_iterator;
536 };
515 537
516 template<class mt_policy = SIGSLOT_DEFAULT_MT_POLICY> 538 template<class mt_policy = SIGSLOT_DEFAULT_MT_POLICY>
517 class has_slots : public has_slots_interface, public mt_policy 539 class has_slots : public has_slots_interface, public mt_policy
518 { 540 {
519 private: 541 private:
520 typedef std::set< _signal_base_interface* > sender_set; 542 typedef std::set< _signal_base_interface* > sender_set;
521 typedef sender_set::const_iterator const_iterator; 543 typedef sender_set::const_iterator const_iterator;
522 544
523 public: 545 public:
524 has_slots() : has_slots_interface(&has_slots::do_signal_connect, &has_slots::do_signal_disconnect, &has_slots::do_disconnect_all) 546 has_slots() : has_slots_interface(&has_slots::do_signal_connect, &has_slots::do_signal_disconnect, &has_slots::do_disconnect_all)
(...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after
598 void connect(desttype* pclass, void (desttype::*pmemfun)(Args... )) 620 void connect(desttype* pclass, void (desttype::*pmemfun)(Args... ))
599 { 621 {
600 lock_block<mt_policy> lock(this); 622 lock_block<mt_policy> lock(this);
601 this->m_connected_slots.push_back(_opaque_connection(pcl ass, pmemfun)); 623 this->m_connected_slots.push_back(_opaque_connection(pcl ass, pmemfun));
602 pclass->signal_connect(static_cast< _signal_base_interfa ce* >(this)); 624 pclass->signal_connect(static_cast< _signal_base_interfa ce* >(this));
603 } 625 }
604 626
605 void emit(Args... args) 627 void emit(Args... args)
606 { 628 {
607 lock_block<mt_policy> lock(this); 629 lock_block<mt_policy> lock(this);
608 » » » typename connections_list::const_iterator it = this->m_c onnected_slots.begin(); 630 this->m_current_iterator =
609 » » » typename connections_list::const_iterator itEnd = this-> m_connected_slots.end(); 631 this->m_connected_slots.begin();
610 632 while (this->m_current_iterator !=
611 » » » while(it != itEnd) 633 this->m_connected_slots.end()) {
612 » » » { 634 _opaque_connection const& conn =
613 » » » » _opaque_connection const& conn = *it; 635 *this->m_current_iterator;
614 » » » » ++it; 636 ++(this->m_current_iterator);
615 637 conn.emit<Args...>(args...);
616 » » » » conn.emit<Args...>(args...); 638 }
617 » » » }
618 } 639 }
619 640
620 void operator()(Args... args) 641 void operator()(Args... args)
621 { 642 {
622 emit(args...); 643 emit(args...);
623 } 644 }
624 }; 645 };
625 646
626 // Alias with default thread policy. Needed because both default argumen ts 647 // Alias with default thread policy. Needed because both default argumen ts
627 // and variadic template arguments must go at the end of the list, so we 648 // and variadic template arguments must go at the end of the list, so we
(...skipping 28 matching lines...) Expand all
656 677
657 template<typename A1, typename A2, typename A3, typename A4, typename A5 , typename A6, typename A7, typename mt_policy = SIGSLOT_DEFAULT_MT_POLICY> 678 template<typename A1, typename A2, typename A3, typename A4, typename A5 , typename A6, typename A7, typename mt_policy = SIGSLOT_DEFAULT_MT_POLICY>
658 using signal7 = signal_with_thread_policy<mt_policy, A1, A2, A3, A4, A5, A6, A7>; 679 using signal7 = signal_with_thread_policy<mt_policy, A1, A2, A3, A4, A5, A6, A7>;
659 680
660 template<typename A1, typename A2, typename A3, typename A4, typename A5 , typename A6, typename A7, typename A8, typename mt_policy = SIGSLOT_DEFAULT_MT _POLICY> 681 template<typename A1, typename A2, typename A3, typename A4, typename A5 , typename A6, typename A7, typename A8, typename mt_policy = SIGSLOT_DEFAULT_MT _POLICY>
661 using signal8 = signal_with_thread_policy<mt_policy, A1, A2, A3, A4, A5, A6, A7, A8>; 682 using signal8 = signal_with_thread_policy<mt_policy, A1, A2, A3, A4, A5, A6, A7, A8>;
662 683
663 } // namespace sigslot 684 } // namespace sigslot
664 685
665 #endif // WEBRTC_BASE_SIGSLOT_H__ 686 #endif // WEBRTC_BASE_SIGSLOT_H__
OLDNEW
« no previous file with comments | « webrtc/base/asyncinvoker.cc ('k') | webrtc/base/sigslot_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698