sigslot.h 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643
  1. // sigslot.h: Signal/Slot classes
  2. //
  3. // Written by Sarah Thompson (sarah@telergy.com) 2002.
  4. //
  5. // License: Public domain. You are free to use this code however you like, with
  6. // the proviso that the author takes on no responsibility or liability for any
  7. // use.
  8. //
  9. // QUICK DOCUMENTATION
  10. //
  11. // (see also the full documentation at http://sigslot.sourceforge.net/)
  12. //
  13. // #define switches
  14. // SIGSLOT_PURE_ISO:
  15. // Define this to force ISO C++ compliance. This also disables all of
  16. // the thread safety support on platforms where it is available.
  17. //
  18. // SIGSLOT_USE_POSIX_THREADS:
  19. // Force use of Posix threads when using a C++ compiler other than gcc
  20. // on a platform that supports Posix threads. (When using gcc, this is
  21. // the default - use SIGSLOT_PURE_ISO to disable this if necessary)
  22. //
  23. // SIGSLOT_DEFAULT_MT_POLICY:
  24. // Where thread support is enabled, this defaults to
  25. // multi_threaded_global. Otherwise, the default is single_threaded.
  26. // #define this yourself to override the default. In pure ISO mode,
  27. // anything other than single_threaded will cause a compiler error.
  28. //
  29. // PLATFORM NOTES
  30. //
  31. // Win32:
  32. // On Win32, the WEBRTC_WIN symbol must be #defined. Most mainstream
  33. // compilers do this by default, but you may need to define it yourself
  34. // if your build environment is less standard. This causes the Win32
  35. // thread support to be compiled in and used automatically.
  36. //
  37. // Unix/Linux/BSD, etc.:
  38. // If you're using gcc, it is assumed that you have Posix threads
  39. // available, so they are used automatically. You can override this (as
  40. // under Windows) with the SIGSLOT_PURE_ISO switch. If you're using
  41. // something other than gcc but still want to use Posix threads, you
  42. // need to #define SIGSLOT_USE_POSIX_THREADS.
  43. //
  44. // ISO C++:
  45. // If none of the supported platforms are detected, or if
  46. // SIGSLOT_PURE_ISO is defined, all multithreading support is turned
  47. // off, along with any code that might cause a pure ISO C++ environment
  48. // to complain. Before you ask, gcc -ansi -pedantic won't compile this
  49. // library, but gcc -ansi is fine. Pedantic mode seems to throw a lot of
  50. // errors that aren't really there. If you feel like investigating this,
  51. // please contact the author.
  52. //
  53. //
  54. // THREADING MODES
  55. //
  56. // single_threaded:
  57. // Your program is assumed to be single threaded from the point of view
  58. // of signal/slot usage (i.e. all objects using signals and slots are
  59. // created and destroyed from a single thread). Behaviour if objects are
  60. // destroyed concurrently is undefined (i.e. you'll get the occasional
  61. // segmentation fault/memory exception).
  62. //
  63. // multi_threaded_global:
  64. // Your program is assumed to be multi threaded. Objects using signals
  65. // and slots can be safely created and destroyed from any thread, even
  66. // when connections exist. In multi_threaded_global mode, this is
  67. // achieved by a single global mutex (actually a critical section on
  68. // Windows because they are faster). This option uses less OS resources,
  69. // but results in more opportunities for contention, possibly resulting
  70. // in more context switches than are strictly necessary.
  71. //
  72. // multi_threaded_local:
  73. // Behaviour in this mode is essentially the same as
  74. // multi_threaded_global, except that each signal, and each object that
  75. // inherits has_slots, all have their own mutex/critical section. In
  76. // practice, this means that mutex collisions (and hence context
  77. // switches) only happen if they are absolutely essential. However, on
  78. // some platforms, creating a lot of mutexes can slow down the whole OS,
  79. // so use this option with care.
  80. //
  81. // USING THE LIBRARY
  82. //
  83. // See the full documentation at http://sigslot.sourceforge.net/
  84. //
  85. // Libjingle specific:
  86. //
  87. // This file has been modified such that has_slots and signalx do not have to be
  88. // using the same threading requirements. E.g. it is possible to connect a
  89. // has_slots<single_threaded> and signal0<multi_threaded_local> or
  90. // has_slots<multi_threaded_local> and signal0<single_threaded>.
  91. // If has_slots is single threaded the user must ensure that it is not trying
  92. // to connect or disconnect to signalx concurrently or data race may occur.
  93. // If signalx is single threaded the user must ensure that disconnect, connect
  94. // or signal is not happening concurrently or data race may occur.
  95. #ifndef RTC_BASE_THIRD_PARTY_SIGSLOT_SIGSLOT_H_
  96. #define RTC_BASE_THIRD_PARTY_SIGSLOT_SIGSLOT_H_
  97. #include <cstring>
  98. #include <list>
  99. #include <set>
  100. // On our copy of sigslot.h, we set single threading as default.
  101. #define SIGSLOT_DEFAULT_MT_POLICY single_threaded
  102. #if defined(SIGSLOT_PURE_ISO) || \
  103. (!defined(WEBRTC_WIN) && !defined(__GNUG__) && \
  104. !defined(SIGSLOT_USE_POSIX_THREADS))
  105. #define _SIGSLOT_SINGLE_THREADED
  106. #elif defined(WEBRTC_WIN)
  107. #define _SIGSLOT_HAS_WIN32_THREADS
  108. #include "windows.h"
  109. #elif defined(__GNUG__) || defined(SIGSLOT_USE_POSIX_THREADS)
  110. #define _SIGSLOT_HAS_POSIX_THREADS
  111. #include <pthread.h>
  112. #else
  113. #define _SIGSLOT_SINGLE_THREADED
  114. #endif
  115. #ifndef SIGSLOT_DEFAULT_MT_POLICY
  116. #ifdef _SIGSLOT_SINGLE_THREADED
  117. #define SIGSLOT_DEFAULT_MT_POLICY single_threaded
  118. #else
  119. #define SIGSLOT_DEFAULT_MT_POLICY multi_threaded_local
  120. #endif
  121. #endif
  122. // TODO: change this namespace to rtc?
  123. namespace sigslot {
  124. class single_threaded {
  125. public:
  126. void lock() {}
  127. void unlock() {}
  128. };
  129. #ifdef _SIGSLOT_HAS_WIN32_THREADS
  130. // The multi threading policies only get compiled in if they are enabled.
  131. class multi_threaded_global {
  132. public:
  133. multi_threaded_global() {
  134. static bool isinitialised = false;
  135. if (!isinitialised) {
  136. InitializeCriticalSection(get_critsec());
  137. isinitialised = true;
  138. }
  139. }
  140. void lock() { EnterCriticalSection(get_critsec()); }
  141. void unlock() { LeaveCriticalSection(get_critsec()); }
  142. private:
  143. CRITICAL_SECTION* get_critsec() {
  144. static CRITICAL_SECTION g_critsec;
  145. return &g_critsec;
  146. }
  147. };
  148. class multi_threaded_local {
  149. public:
  150. multi_threaded_local() { InitializeCriticalSection(&m_critsec); }
  151. multi_threaded_local(const multi_threaded_local&) {
  152. InitializeCriticalSection(&m_critsec);
  153. }
  154. ~multi_threaded_local() { DeleteCriticalSection(&m_critsec); }
  155. void lock() { EnterCriticalSection(&m_critsec); }
  156. void unlock() { LeaveCriticalSection(&m_critsec); }
  157. private:
  158. CRITICAL_SECTION m_critsec;
  159. };
  160. #endif // _SIGSLOT_HAS_WIN32_THREADS
  161. #ifdef _SIGSLOT_HAS_POSIX_THREADS
  162. // The multi threading policies only get compiled in if they are enabled.
  163. class multi_threaded_global {
  164. public:
  165. void lock() { pthread_mutex_lock(get_mutex()); }
  166. void unlock() { pthread_mutex_unlock(get_mutex()); }
  167. private:
  168. static pthread_mutex_t* get_mutex();
  169. };
  170. class multi_threaded_local {
  171. public:
  172. multi_threaded_local() { pthread_mutex_init(&m_mutex, nullptr); }
  173. multi_threaded_local(const multi_threaded_local&) {
  174. pthread_mutex_init(&m_mutex, nullptr);
  175. }
  176. ~multi_threaded_local() { pthread_mutex_destroy(&m_mutex); }
  177. void lock() { pthread_mutex_lock(&m_mutex); }
  178. void unlock() { pthread_mutex_unlock(&m_mutex); }
  179. private:
  180. pthread_mutex_t m_mutex;
  181. };
  182. #endif // _SIGSLOT_HAS_POSIX_THREADS
  183. template <class mt_policy>
  184. class lock_block {
  185. public:
  186. mt_policy* m_mutex;
  187. lock_block(mt_policy* mtx) : m_mutex(mtx) { m_mutex->lock(); }
  188. ~lock_block() { m_mutex->unlock(); }
  189. };
  190. class _signal_base_interface;
  191. class has_slots_interface {
  192. private:
  193. typedef void (*signal_connect_t)(has_slots_interface* self,
  194. _signal_base_interface* sender);
  195. typedef void (*signal_disconnect_t)(has_slots_interface* self,
  196. _signal_base_interface* sender);
  197. typedef void (*disconnect_all_t)(has_slots_interface* self);
  198. const signal_connect_t m_signal_connect;
  199. const signal_disconnect_t m_signal_disconnect;
  200. const disconnect_all_t m_disconnect_all;
  201. protected:
  202. has_slots_interface(signal_connect_t conn,
  203. signal_disconnect_t disc,
  204. disconnect_all_t disc_all)
  205. : m_signal_connect(conn),
  206. m_signal_disconnect(disc),
  207. m_disconnect_all(disc_all) {}
  208. // Doesn't really need to be virtual, but is for backwards compatibility
  209. // (it was virtual in a previous version of sigslot).
  210. virtual ~has_slots_interface() {}
  211. public:
  212. void signal_connect(_signal_base_interface* sender) {
  213. m_signal_connect(this, sender);
  214. }
  215. void signal_disconnect(_signal_base_interface* sender) {
  216. m_signal_disconnect(this, sender);
  217. }
  218. void disconnect_all() { m_disconnect_all(this); }
  219. };
  220. class _signal_base_interface {
  221. private:
  222. typedef void (*slot_disconnect_t)(_signal_base_interface* self,
  223. has_slots_interface* pslot);
  224. typedef void (*slot_duplicate_t)(_signal_base_interface* self,
  225. const has_slots_interface* poldslot,
  226. has_slots_interface* pnewslot);
  227. const slot_disconnect_t m_slot_disconnect;
  228. const slot_duplicate_t m_slot_duplicate;
  229. protected:
  230. _signal_base_interface(slot_disconnect_t disc, slot_duplicate_t dupl)
  231. : m_slot_disconnect(disc), m_slot_duplicate(dupl) {}
  232. ~_signal_base_interface() {}
  233. public:
  234. void slot_disconnect(has_slots_interface* pslot) {
  235. m_slot_disconnect(this, pslot);
  236. }
  237. void slot_duplicate(const has_slots_interface* poldslot,
  238. has_slots_interface* pnewslot) {
  239. m_slot_duplicate(this, poldslot, pnewslot);
  240. }
  241. };
  242. class _opaque_connection {
  243. private:
  244. typedef void (*emit_t)(const _opaque_connection*);
  245. template <typename FromT, typename ToT>
  246. union union_caster {
  247. FromT from;
  248. ToT to;
  249. };
  250. emit_t pemit;
  251. has_slots_interface* pdest;
  252. // Pointers to member functions may be up to 16 bytes for virtual classes,
  253. // so make sure we have enough space to store it.
  254. unsigned char pmethod[16];
  255. public:
  256. template <typename DestT, typename... Args>
  257. _opaque_connection(DestT* pd, void (DestT::*pm)(Args...)) : pdest(pd) {
  258. typedef void (DestT::*pm_t)(Args...);
  259. static_assert(sizeof(pm_t) <= sizeof(pmethod),
  260. "Size of slot function pointer too large.");
  261. std::memcpy(pmethod, &pm, sizeof(pm_t));
  262. typedef void (*em_t)(const _opaque_connection* self, Args...);
  263. union_caster<em_t, emit_t> caster2;
  264. caster2.from = &_opaque_connection::emitter<DestT, Args...>;
  265. pemit = caster2.to;
  266. }
  267. has_slots_interface* getdest() const { return pdest; }
  268. _opaque_connection duplicate(has_slots_interface* newtarget) const {
  269. _opaque_connection res = *this;
  270. res.pdest = newtarget;
  271. return res;
  272. }
  273. // Just calls the stored "emitter" function pointer stored at construction
  274. // time.
  275. template <typename... Args>
  276. void emit(Args... args) const {
  277. typedef void (*em_t)(const _opaque_connection*, Args...);
  278. union_caster<emit_t, em_t> caster;
  279. caster.from = pemit;
  280. (caster.to)(this, args...);
  281. }
  282. private:
  283. template <typename DestT, typename... Args>
  284. static void emitter(const _opaque_connection* self, Args... args) {
  285. typedef void (DestT::*pm_t)(Args...);
  286. pm_t pm;
  287. std::memcpy(&pm, self->pmethod, sizeof(pm_t));
  288. (static_cast<DestT*>(self->pdest)->*(pm))(args...);
  289. }
  290. };
  291. template <class mt_policy>
  292. class _signal_base : public _signal_base_interface, public mt_policy {
  293. protected:
  294. typedef std::list<_opaque_connection> connections_list;
  295. _signal_base()
  296. : _signal_base_interface(&_signal_base::do_slot_disconnect,
  297. &_signal_base::do_slot_duplicate),
  298. m_current_iterator(m_connected_slots.end()) {}
  299. ~_signal_base() { disconnect_all(); }
  300. private:
  301. _signal_base& operator=(_signal_base const& that);
  302. public:
  303. _signal_base(const _signal_base& o)
  304. : _signal_base_interface(&_signal_base::do_slot_disconnect,
  305. &_signal_base::do_slot_duplicate),
  306. m_current_iterator(m_connected_slots.end()) {
  307. lock_block<mt_policy> lock(this);
  308. for (const auto& connection : o.m_connected_slots) {
  309. connection.getdest()->signal_connect(this);
  310. m_connected_slots.push_back(connection);
  311. }
  312. }
  313. bool is_empty() {
  314. lock_block<mt_policy> lock(this);
  315. return m_connected_slots.empty();
  316. }
  317. void disconnect_all() {
  318. lock_block<mt_policy> lock(this);
  319. while (!m_connected_slots.empty()) {
  320. has_slots_interface* pdest = m_connected_slots.front().getdest();
  321. m_connected_slots.pop_front();
  322. pdest->signal_disconnect(static_cast<_signal_base_interface*>(this));
  323. }
  324. // If disconnect_all is called while the signal is firing, advance the
  325. // current slot iterator to the end to avoid an invalidated iterator from
  326. // being dereferenced.
  327. m_current_iterator = m_connected_slots.end();
  328. }
  329. #if !defined(NDEBUG)
  330. bool connected(has_slots_interface* pclass) {
  331. lock_block<mt_policy> lock(this);
  332. connections_list::const_iterator it = m_connected_slots.begin();
  333. connections_list::const_iterator itEnd = m_connected_slots.end();
  334. while (it != itEnd) {
  335. if (it->getdest() == pclass)
  336. return true;
  337. ++it;
  338. }
  339. return false;
  340. }
  341. #endif
  342. void disconnect(has_slots_interface* pclass) {
  343. lock_block<mt_policy> lock(this);
  344. connections_list::iterator it = m_connected_slots.begin();
  345. connections_list::iterator itEnd = m_connected_slots.end();
  346. while (it != itEnd) {
  347. if (it->getdest() == pclass) {
  348. // If we're currently using this iterator because the signal is firing,
  349. // advance it to avoid it being invalidated.
  350. if (m_current_iterator == it) {
  351. m_current_iterator = m_connected_slots.erase(it);
  352. } else {
  353. m_connected_slots.erase(it);
  354. }
  355. pclass->signal_disconnect(static_cast<_signal_base_interface*>(this));
  356. return;
  357. }
  358. ++it;
  359. }
  360. }
  361. private:
  362. static void do_slot_disconnect(_signal_base_interface* p,
  363. has_slots_interface* pslot) {
  364. _signal_base* const self = static_cast<_signal_base*>(p);
  365. lock_block<mt_policy> lock(self);
  366. connections_list::iterator it = self->m_connected_slots.begin();
  367. connections_list::iterator itEnd = self->m_connected_slots.end();
  368. while (it != itEnd) {
  369. connections_list::iterator itNext = it;
  370. ++itNext;
  371. if (it->getdest() == pslot) {
  372. // If we're currently using this iterator because the signal is firing,
  373. // advance it to avoid it being invalidated.
  374. if (self->m_current_iterator == it) {
  375. self->m_current_iterator = self->m_connected_slots.erase(it);
  376. } else {
  377. self->m_connected_slots.erase(it);
  378. }
  379. }
  380. it = itNext;
  381. }
  382. }
  383. static void do_slot_duplicate(_signal_base_interface* p,
  384. const has_slots_interface* oldtarget,
  385. has_slots_interface* newtarget) {
  386. _signal_base* const self = static_cast<_signal_base*>(p);
  387. lock_block<mt_policy> lock(self);
  388. connections_list::iterator it = self->m_connected_slots.begin();
  389. connections_list::iterator itEnd = self->m_connected_slots.end();
  390. while (it != itEnd) {
  391. if (it->getdest() == oldtarget) {
  392. self->m_connected_slots.push_back(it->duplicate(newtarget));
  393. }
  394. ++it;
  395. }
  396. }
  397. protected:
  398. connections_list m_connected_slots;
  399. // Used to handle a slot being disconnected while a signal is
  400. // firing (iterating m_connected_slots).
  401. connections_list::iterator m_current_iterator;
  402. bool m_erase_current_iterator = false;
  403. };
  404. template <class mt_policy = SIGSLOT_DEFAULT_MT_POLICY>
  405. class has_slots : public has_slots_interface, public mt_policy {
  406. private:
  407. typedef std::set<_signal_base_interface*> sender_set;
  408. typedef sender_set::const_iterator const_iterator;
  409. public:
  410. has_slots()
  411. : has_slots_interface(&has_slots::do_signal_connect,
  412. &has_slots::do_signal_disconnect,
  413. &has_slots::do_disconnect_all) {}
  414. has_slots(has_slots const& o)
  415. : has_slots_interface(&has_slots::do_signal_connect,
  416. &has_slots::do_signal_disconnect,
  417. &has_slots::do_disconnect_all) {
  418. lock_block<mt_policy> lock(this);
  419. for (auto* sender : o.m_senders) {
  420. sender->slot_duplicate(&o, this);
  421. m_senders.insert(sender);
  422. }
  423. }
  424. ~has_slots() { this->disconnect_all(); }
  425. private:
  426. has_slots& operator=(has_slots const&);
  427. static void do_signal_connect(has_slots_interface* p,
  428. _signal_base_interface* sender) {
  429. has_slots* const self = static_cast<has_slots*>(p);
  430. lock_block<mt_policy> lock(self);
  431. self->m_senders.insert(sender);
  432. }
  433. static void do_signal_disconnect(has_slots_interface* p,
  434. _signal_base_interface* sender) {
  435. has_slots* const self = static_cast<has_slots*>(p);
  436. lock_block<mt_policy> lock(self);
  437. self->m_senders.erase(sender);
  438. }
  439. static void do_disconnect_all(has_slots_interface* p) {
  440. has_slots* const self = static_cast<has_slots*>(p);
  441. lock_block<mt_policy> lock(self);
  442. while (!self->m_senders.empty()) {
  443. std::set<_signal_base_interface*> senders;
  444. senders.swap(self->m_senders);
  445. const_iterator it = senders.begin();
  446. const_iterator itEnd = senders.end();
  447. while (it != itEnd) {
  448. _signal_base_interface* s = *it;
  449. ++it;
  450. s->slot_disconnect(p);
  451. }
  452. }
  453. }
  454. private:
  455. sender_set m_senders;
  456. };
  457. template <class mt_policy, typename... Args>
  458. class signal_with_thread_policy : public _signal_base<mt_policy> {
  459. private:
  460. typedef _signal_base<mt_policy> base;
  461. protected:
  462. typedef typename base::connections_list connections_list;
  463. public:
  464. signal_with_thread_policy() {}
  465. template <class desttype>
  466. void connect(desttype* pclass, void (desttype::*pmemfun)(Args...)) {
  467. lock_block<mt_policy> lock(this);
  468. this->m_connected_slots.push_back(_opaque_connection(pclass, pmemfun));
  469. pclass->signal_connect(static_cast<_signal_base_interface*>(this));
  470. }
  471. void emit(Args... args) {
  472. lock_block<mt_policy> lock(this);
  473. this->m_current_iterator = this->m_connected_slots.begin();
  474. while (this->m_current_iterator != this->m_connected_slots.end()) {
  475. _opaque_connection const& conn = *this->m_current_iterator;
  476. ++(this->m_current_iterator);
  477. conn.emit<Args...>(args...);
  478. }
  479. }
  480. void operator()(Args... args) { emit(args...); }
  481. };
  482. // Alias with default thread policy. Needed because both default arguments
  483. // and variadic template arguments must go at the end of the list, so we
  484. // can't have both at once.
  485. template <typename... Args>
  486. using signal = signal_with_thread_policy<SIGSLOT_DEFAULT_MT_POLICY, Args...>;
  487. // The previous verion of sigslot didn't use variadic templates, so you would
  488. // need to write "sigslot::signal2<Arg1, Arg2>", for example.
  489. // Now you can just write "sigslot::signal<Arg1, Arg2>", but these aliases
  490. // exist for backwards compatibility.
  491. template <typename mt_policy = SIGSLOT_DEFAULT_MT_POLICY>
  492. using signal0 = signal_with_thread_policy<mt_policy>;
  493. template <typename A1, typename mt_policy = SIGSLOT_DEFAULT_MT_POLICY>
  494. using signal1 = signal_with_thread_policy<mt_policy, A1>;
  495. template <typename A1,
  496. typename A2,
  497. typename mt_policy = SIGSLOT_DEFAULT_MT_POLICY>
  498. using signal2 = signal_with_thread_policy<mt_policy, A1, A2>;
  499. template <typename A1,
  500. typename A2,
  501. typename A3,
  502. typename mt_policy = SIGSLOT_DEFAULT_MT_POLICY>
  503. using signal3 = signal_with_thread_policy<mt_policy, A1, A2, A3>;
  504. template <typename A1,
  505. typename A2,
  506. typename A3,
  507. typename A4,
  508. typename mt_policy = SIGSLOT_DEFAULT_MT_POLICY>
  509. using signal4 = signal_with_thread_policy<mt_policy, A1, A2, A3, A4>;
  510. template <typename A1,
  511. typename A2,
  512. typename A3,
  513. typename A4,
  514. typename A5,
  515. typename mt_policy = SIGSLOT_DEFAULT_MT_POLICY>
  516. using signal5 = signal_with_thread_policy<mt_policy, A1, A2, A3, A4, A5>;
  517. template <typename A1,
  518. typename A2,
  519. typename A3,
  520. typename A4,
  521. typename A5,
  522. typename A6,
  523. typename mt_policy = SIGSLOT_DEFAULT_MT_POLICY>
  524. using signal6 = signal_with_thread_policy<mt_policy, A1, A2, A3, A4, A5, A6>;
  525. template <typename A1,
  526. typename A2,
  527. typename A3,
  528. typename A4,
  529. typename A5,
  530. typename A6,
  531. typename A7,
  532. typename mt_policy = SIGSLOT_DEFAULT_MT_POLICY>
  533. using signal7 =
  534. signal_with_thread_policy<mt_policy, A1, A2, A3, A4, A5, A6, A7>;
  535. template <typename A1,
  536. typename A2,
  537. typename A3,
  538. typename A4,
  539. typename A5,
  540. typename A6,
  541. typename A7,
  542. typename A8,
  543. typename mt_policy = SIGSLOT_DEFAULT_MT_POLICY>
  544. using signal8 =
  545. signal_with_thread_policy<mt_policy, A1, A2, A3, A4, A5, A6, A7, A8>;
  546. } // namespace sigslot
  547. #endif /* RTC_BASE_THIRD_PARTY_SIGSLOT_SIGSLOT_H_ */