1 ////////////////////////////////////////////////////////////////////////////////
2 // Copyright (c) 2011 Bryce Adelstein-Lelbach
3 // Copyright (c) 2012-2017 Hartmut Kaiser
4 // Copyright (c) 2016 Thomas Heller
5 //
6 // Distributed under the Boost Software License, Version 1.0. (See accompanying
7 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
8 ////////////////////////////////////////////////////////////////////////////////
9
10 #include <hpx/config.hpp>
11 #include <hpx/lcos/base_lco_with_value.hpp>
12 #include <hpx/performance_counters/counter_creators.hpp>
13 #include <hpx/performance_counters/counters.hpp>
14 #include <hpx/performance_counters/manage_counter_type.hpp>
15 #include <hpx/runtime/agas/interface.hpp>
16 #include <hpx/runtime/agas/namespace_action_code.hpp>
17 #include <hpx/runtime/agas/server/symbol_namespace.hpp>
18 #include <hpx/runtime/naming/split_gid.hpp>
19 #include <hpx/throw_exception.hpp>
20 #include <hpx/util/assert.hpp>
21 #include <hpx/util/bind_back.hpp>
22 #include <hpx/util/bind_front.hpp>
23 #include <hpx/util/format.hpp>
24 #include <hpx/util/get_and_reset_value.hpp>
25 #include <hpx/util/insert_checked.hpp>
26 #include <hpx/util/regex_from_pattern.hpp>
27 #include <hpx/util/scoped_timer.hpp>
28 #include <hpx/util/unlock_guard.hpp>
29
30 #include <atomic>
31 #include <cstddef>
32 #include <cstdint>
33 #include <map>
34 #include <memory>
35 #include <mutex>
36 #include <string>
37 #include <utility>
38 #include <vector>
39
40 #include <boost/regex.hpp>
41
42 namespace hpx { namespace agas
43 {
44
bootstrap_symbol_namespace_gid()45 naming::gid_type bootstrap_symbol_namespace_gid()
46 {
47 return naming::gid_type(HPX_AGAS_SYMBOL_NS_MSB, HPX_AGAS_SYMBOL_NS_LSB);
48 }
49
bootstrap_symbol_namespace_id()50 naming::id_type bootstrap_symbol_namespace_id()
51 {
52 return naming::id_type(HPX_AGAS_SYMBOL_NS_MSB, HPX_AGAS_SYMBOL_NS_LSB
53 , naming::id_type::unmanaged);
54 }
55
56 namespace server
57 {
58
59 // register all performance counter types exposed by this component
register_counter_types(error_code & ec)60 void symbol_namespace::register_counter_types(
61 error_code& ec
62 )
63 {
64 performance_counters::create_counter_func creator(
65 util::bind_back(&performance_counters::agas_raw_counter_creator
66 , agas::server::symbol_namespace_service_name));
67
68 for (std::size_t i = 0;
69 i != detail::num_symbol_namespace_services;
70 ++i)
71 {
72 // global counters are handled elsewhere
73 if (detail::symbol_namespace_services[i].code_ == symbol_ns_statistics_counter)
74 continue;
75
76 std::string name(detail::symbol_namespace_services[i].name_);
77 std::string help;
78 std::string::size_type p = name.find_last_of('/');
79 HPX_ASSERT(p != std::string::npos);
80
81 if (detail::symbol_namespace_services[i].target_
82 == detail::counter_target_count)
83 help = hpx::util::format(
84 "returns the number of invocations of the AGAS service '{}'",
85 name.substr(p+1));
86 else
87 help = hpx::util::format(
88 "returns the overall execution time of the AGAS service '{}'",
89 name.substr(p+1));
90
91 performance_counters::install_counter_type(
92 agas::performance_counter_basename + name
93 , performance_counters::counter_raw
94 , help
95 , creator
96 , &performance_counters::locality_counter_discoverer
97 , HPX_PERFORMANCE_COUNTER_V1
98 , detail::symbol_namespace_services[i].uom_
99 , ec
100 );
101 if (ec) return;
102 }
103 }
104
register_global_counter_types(error_code & ec)105 void symbol_namespace::register_global_counter_types(
106 error_code& ec
107 )
108 {
109 performance_counters::create_counter_func creator(
110 util::bind_back(&performance_counters::agas_raw_counter_creator
111 , agas::server::symbol_namespace_service_name));
112
113 for (std::size_t i = 0;
114 i != detail::num_symbol_namespace_services;
115 ++i)
116 {
117 // local counters are handled elsewhere
118 if (detail::symbol_namespace_services[i].code_ != symbol_ns_statistics_counter)
119 continue;
120
121 std::string help;
122 if (detail::symbol_namespace_services[i].target_ == detail::counter_target_count)
123 help = "returns the overall number of invocations \
124 of all symbol AGAS services";
125 else
126 help = "returns the overall execution time of all symbol AGAS services";
127
128 performance_counters::install_counter_type(
129 std::string(agas::performance_counter_basename) +
130 detail::symbol_namespace_services[i].name_
131 , performance_counters::counter_raw
132 , help
133 , creator
134 , &performance_counters::locality_counter_discoverer
135 , HPX_PERFORMANCE_COUNTER_V1
136 , detail::symbol_namespace_services[i].uom_
137 , ec
138 );
139 if (ec) return;
140 }
141 }
142
register_server_instance(char const * servicename,std::uint32_t locality_id,error_code & ec)143 void symbol_namespace::register_server_instance(
144 char const* servicename
145 , std::uint32_t locality_id
146 , error_code& ec
147 )
148 {
149 // set locality_id for this component
150 if (locality_id == naming::invalid_locality_id)
151 locality_id = 0; // if not given, we're on the root
152
153 this->base_type::set_locality_id(locality_id);
154
155 // now register this AGAS instance with AGAS :-P
156 instance_name_ = agas::service_name;
157 instance_name_ += servicename;
158 instance_name_ += agas::server::symbol_namespace_service_name;
159
160 // register a gid (not the id) to avoid AGAS holding a reference to this
161 // component
162 agas::register_name(launch::sync, instance_name_,
163 get_unmanaged_id().get_gid(), ec);
164 }
165
unregister_server_instance(error_code & ec)166 void symbol_namespace::unregister_server_instance(
167 error_code& ec
168 )
169 {
170 agas::unregister_name(launch::sync, instance_name_, ec);
171 this->base_type::finalize();
172 }
173
finalize()174 void symbol_namespace::finalize()
175 {
176 if (!instance_name_.empty())
177 {
178 error_code ec(lightweight);
179 agas::unregister_name(launch::sync, instance_name_, ec);
180 }
181 }
182
bind(std::string key,naming::gid_type gid)183 bool symbol_namespace::bind(
184 std::string key
185 , naming::gid_type gid
186 )
187 { // {{{ bind implementation
188 // parameters
189 util::scoped_timer<std::atomic<std::int64_t> > update(
190 counter_data_.bind_.time_,
191 counter_data_.bind_.enabled_
192 );
193 counter_data_.increment_bind_count();
194
195 std::unique_lock<mutex_type> l(mutex_);
196
197 gid_table_type::iterator it = gids_.find(key);
198 gid_table_type::iterator end = gids_.end();
199
200 if (it != end)
201 {
202 std::int64_t const credits = naming::detail::get_credit_from_gid(gid);
203 naming::gid_type raw_gid = *(it->second);
204
205 naming::detail::strip_internal_bits_from_gid(raw_gid);
206 naming::detail::strip_internal_bits_from_gid(gid);
207
208 // increase reference count
209 if (raw_gid == gid)
210 {
211 LAGAS_(info) << hpx::util::format(
212 "symbol_namespace::bind, key({1}), gid({2}), old_credit({3}), "
213 "new_credit({4})",
214 key, gid,
215 naming::detail::get_credit_from_gid(*(it->second)),
216 naming::detail::get_credit_from_gid(*(it->second)) + credits);
217
218 // REVIEW: do we need to add the credit of the argument to the table?
219 naming::detail::add_credit_to_gid(*(it->second), credits);
220
221 return true;
222 }
223
224 if (LAGAS_ENABLED(info))
225 {
226 naming::detail::add_credit_to_gid(gid, credits);
227 LAGAS_(info) << hpx::util::format(
228 "symbol_namespace::bind, key({1}), gid({2}), response(no_success)",
229 key, gid);
230 }
231
232 return false;
233 }
234
235 if (HPX_UNLIKELY(!util::insert_checked(gids_.insert(
236 std::make_pair(key, std::make_shared<naming::gid_type>(gid))))))
237 {
238 l.unlock();
239
240 HPX_THROW_EXCEPTION(lock_error
241 , "symbol_namespace::bind"
242 , "GID table insertion failed due to a locking error or "
243 "memory corruption");
244 }
245
246 // handle registered events
247 typedef on_event_data_map_type::iterator iterator;
248 std::pair<iterator, iterator> p = on_event_data_.equal_range(key);
249
250 std::vector<hpx::id_type> lcos;
251 if (p.first != p.second)
252 {
253 iterator it = p.first;
254 while (it != p.second)
255 {
256 lcos.push_back((*it).second);
257 ++it;
258 }
259
260 on_event_data_.erase(p.first, p.second);
261
262 // notify all LCOS which were registered with this name
263 for (hpx::id_type const& id : lcos)
264 {
265 // re-locate the entry in the GID table for each LCO anew, as we
266 // need to unlock the mutex protecting the table for each iteration
267 // below
268 gid_table_type::iterator gid_it = gids_.find(key);
269 if (gid_it == gids_.end())
270 {
271 l.unlock();
272
273 HPX_THROW_EXCEPTION(invalid_status
274 , "symbol_namespace::bind"
275 , "unable to re-locate the entry in the GID table");
276 }
277
278 // hold on to the gid while the map is unlocked
279 std::shared_ptr<naming::gid_type> current_gid = gid_it->second;
280
281 {
282 util::unlock_guard<std::unique_lock<mutex_type> > ul(l);
283
284 // split the credit as the receiving end will expect to keep the
285 // object alive
286 naming::gid_type new_gid =
287 naming::detail::split_gid_if_needed(*current_gid).get();
288
289 // trigger the lco
290 set_lco_value(id, std::move(new_gid));
291 }
292 }
293 }
294
295 l.unlock();
296
297 LAGAS_(info) << hpx::util::format(
298 "symbol_namespace::bind, key({1}), gid({2})",
299 key, gid);
300
301 return true;
302 } // }}}
303
resolve(std::string const & key)304 naming::gid_type symbol_namespace::resolve(std::string const& key)
305 { // {{{ resolve implementation
306 // parameters
307 util::scoped_timer<std::atomic<std::int64_t> > update(
308 counter_data_.resolve_.time_,
309 counter_data_.resolve_.enabled_
310 );
311 counter_data_.increment_resolve_count();
312
313 std::unique_lock<mutex_type> l(mutex_);
314
315 gid_table_type::iterator it = gids_.find(key);
316 gid_table_type::iterator end = gids_.end();
317
318 if (it == end)
319 {
320 LAGAS_(info) << hpx::util::format(
321 "symbol_namespace::resolve, key({1}), response(no_success)",
322 key);
323
324 return naming::invalid_gid;
325 }
326
327 // hold on to gid before unlocking the map
328 std::shared_ptr<naming::gid_type> current_gid(it->second);
329
330 l.unlock();
331 naming::gid_type gid = naming::detail::split_gid_if_needed(*current_gid).get();
332
333 LAGAS_(info) << hpx::util::format(
334 "symbol_namespace::resolve, key({1}), gid({2})",
335 key, gid);
336
337 return gid;
338 } // }}}
339
unbind(std::string const & key)340 naming::gid_type symbol_namespace::unbind(std::string const& key)
341 { // {{{ unbind implementation
342 util::scoped_timer<std::atomic<std::int64_t> > update(
343 counter_data_.unbind_.time_,
344 counter_data_.unbind_.enabled_
345 );
346 counter_data_.increment_unbind_count();
347
348 std::lock_guard<mutex_type> l(mutex_);
349
350 gid_table_type::iterator it = gids_.find(key);
351 gid_table_type::iterator end = gids_.end();
352
353 if (it == end)
354 {
355 LAGAS_(info) << hpx::util::format(
356 "symbol_namespace::unbind, key({1}), response(no_success)",
357 key);
358
359 return naming::invalid_gid;
360 }
361
362 naming::gid_type const gid = *(it->second);
363
364 gids_.erase(it);
365
366 LAGAS_(info) << hpx::util::format(
367 "symbol_namespace::unbind, key({1}), gid({2})",
368 key, gid);
369
370 return gid;
371 } // }}}
372
373 // TODO: catch exceptions
iterate(std::string const & pattern)374 symbol_namespace::iterate_names_return_type symbol_namespace::iterate(
375 std::string const& pattern)
376 { // {{{ iterate implementation
377 util::scoped_timer<std::atomic<std::int64_t> > update(
378 counter_data_.iterate_names_.time_,
379 counter_data_.iterate_names_.enabled_
380 );
381 counter_data_.increment_iterate_names_count();
382
383 std::map<std::string, naming::gid_type> found;
384
385 if (pattern.find_first_of("*?[]") != std::string::npos)
386 {
387 std::string str_rx(util::regex_from_pattern(pattern, throws));
388 boost::regex rx(str_rx, boost::regex::perl);
389
390 std::unique_lock<mutex_type> l(mutex_);
391 for (gid_table_type::iterator it = gids_.begin(); it != gids_.end();
392 ++it)
393 {
394 if (!boost::regex_match(it->first, rx))
395 continue;
396
397 // hold on to entry while map is unlocked
398 std::shared_ptr<naming::gid_type> current_gid(it->second);
399 util::unlock_guard<std::unique_lock<mutex_type> > ul(l);
400
401 found[it->first] =
402 naming::detail::split_gid_if_needed(*current_gid).get();
403 }
404 }
405 else
406 {
407 std::unique_lock<mutex_type> l(mutex_);
408 for (gid_table_type::iterator it = gids_.begin(); it != gids_.end();
409 ++it)
410 {
411 if (!pattern.empty() && pattern != it->first)
412 continue;
413
414 // hold on to entry while map is unlocked
415 std::shared_ptr<naming::gid_type> current_gid(it->second);
416 util::unlock_guard<std::unique_lock<mutex_type> > ul(l);
417
418 found[it->first] =
419 naming::detail::split_gid_if_needed(*current_gid).get();
420 }
421 }
422
423 LAGAS_(info) << "symbol_namespace::iterate";
424
425 return found;
426 } // }}}
427
on_event(std::string const & name,bool call_for_past_events,hpx::id_type lco)428 bool symbol_namespace::on_event(
429 std::string const& name
430 , bool call_for_past_events
431 , hpx::id_type lco
432 )
433 { // {{{ on_event implementation
434 util::scoped_timer<std::atomic<std::int64_t> > update(
435 counter_data_.on_event_.time_,
436 counter_data_.on_event_.enabled_
437 );
438 counter_data_.increment_on_event_count();
439
440 std::unique_lock<mutex_type> l(mutex_);
441
442 bool handled = false;
443 if (call_for_past_events)
444 {
445 gid_table_type::iterator it = gids_.find(name);
446 if (it != gids_.end())
447 {
448 // hold on to entry while map is unlocked
449 std::shared_ptr<naming::gid_type> current_gid(it->second);
450
451 // split the credit as the receiving end will expect to keep the
452 // object alive
453 {
454 util::unlock_guard<std::unique_lock<mutex_type> > ul(l);
455 naming::gid_type new_gid = naming::detail::split_gid_if_needed(
456 *current_gid).get();
457
458 // trigger the lco
459 handled = true;
460
461 // trigger LCO as name is already bound to an id
462 set_lco_value(lco, std::move(new_gid));
463 }
464 }
465 }
466
467 if (!handled)
468 {
469 on_event_data_map_type::iterator it = on_event_data_.insert(
470 on_event_data_map_type::value_type(std::move(name), lco));
471
472 // This overload of insert always returns the iterator pointing
473 // to the inserted value. It should never point to end
474 HPX_ASSERT(it != on_event_data_.end());
475 }
476 l.unlock();
477
478 LAGAS_(info) << "symbol_namespace::on_event";
479
480 return true;
481 } // }}}
482
statistics_counter(std::string const & name)483 naming::gid_type symbol_namespace::statistics_counter(std::string const& name)
484 { // {{{ statistics_counter implementation
485 LAGAS_(info) << "symbol_namespace::statistics_counter";
486
487 performance_counters::counter_path_elements p;
488 performance_counters::get_counter_path_elements(name, p);
489
490 if (p.objectname_ != "agas")
491 {
492 HPX_THROW_EXCEPTION(bad_parameter,
493 "symbol_namespace::statistics_counter",
494 "unknown performance counter (unrelated to AGAS)");
495 }
496
497 namespace_action_code code = invalid_request;
498 detail::counter_target target = detail::counter_target_invalid;
499 for (std::size_t i = 0;
500 i != detail::num_symbol_namespace_services;
501 ++i)
502 {
503 if (p.countername_ == detail::symbol_namespace_services[i].name_)
504 {
505 code = detail::symbol_namespace_services[i].code_;
506 target = detail::symbol_namespace_services[i].target_;
507 break;
508 }
509 }
510
511 if (code == invalid_request || target == detail::counter_target_invalid)
512 {
513 HPX_THROW_EXCEPTION(bad_parameter,
514 "symbol_namespace::statistics_counter",
515 "unknown performance counter (unrelated to AGAS)");
516 }
517
518 typedef symbol_namespace::counter_data cd;
519
520 util::function_nonser<std::int64_t(bool)> get_data_func;
521 if (target == detail::counter_target_count)
522 {
523 switch (code) {
524 case symbol_ns_bind:
525 get_data_func = util::bind_front(&cd::get_bind_count,
526 &counter_data_);
527 counter_data_.bind_.enabled_ = true;
528 break;
529 case symbol_ns_resolve:
530 get_data_func = util::bind_front(&cd::get_resolve_count,
531 &counter_data_);
532 counter_data_.resolve_.enabled_ = true;
533 break;
534 case symbol_ns_unbind:
535 get_data_func = util::bind_front(&cd::get_unbind_count,
536 &counter_data_);
537 counter_data_.unbind_.enabled_ = true;
538 break;
539 case symbol_ns_iterate_names:
540 get_data_func = util::bind_front(&cd::get_iterate_names_count,
541 &counter_data_);
542 counter_data_.iterate_names_.enabled_ = true;
543 break;
544 case symbol_ns_on_event:
545 get_data_func = util::bind_front(&cd::get_on_event_count,
546 &counter_data_);
547 counter_data_.on_event_.enabled_ = true;
548 break;
549 case symbol_ns_statistics_counter:
550 get_data_func = util::bind_front(&cd::get_overall_count,
551 &counter_data_);
552 counter_data_.enable_all();
553 break;
554 default:
555 HPX_THROW_EXCEPTION(bad_parameter
556 , "symbol_namespace::statistics"
557 , "bad action code while querying statistics");
558 }
559 }
560 else {
561 HPX_ASSERT(detail::counter_target_time == target);
562 switch (code) {
563 case symbol_ns_bind:
564 get_data_func = util::bind_front(&cd::get_bind_time,
565 &counter_data_);
566 counter_data_.bind_.enabled_ = true;
567 break;
568 case symbol_ns_resolve:
569 get_data_func = util::bind_front(&cd::get_resolve_time,
570 &counter_data_);
571 counter_data_.resolve_.enabled_ = true;
572 break;
573 case symbol_ns_unbind:
574 get_data_func = util::bind_front(&cd::get_unbind_time,
575 &counter_data_);
576 counter_data_.unbind_.enabled_ = true;
577 break;
578 case symbol_ns_iterate_names:
579 get_data_func = util::bind_front(&cd::get_iterate_names_time,
580 &counter_data_);
581 counter_data_.iterate_names_.enabled_ = true;
582 break;
583 case symbol_ns_on_event:
584 get_data_func = util::bind_front(&cd::get_on_event_time,
585 &counter_data_);
586 counter_data_.on_event_.enabled_ = true;
587 break;
588 case symbol_ns_statistics_counter:
589 get_data_func = util::bind_front(&cd::get_overall_time,
590 &counter_data_);
591 counter_data_.enable_all();
592 break;
593 default:
594 HPX_THROW_EXCEPTION(bad_parameter
595 , "symbol_namespace::statistics"
596 , "bad action code while querying statistics");
597 }
598 }
599
600 performance_counters::counter_info info;
601 performance_counters::get_counter_type(name, info);
602
603 performance_counters::complement_counter_info(info);
604
605 using performance_counters::detail::create_raw_counter;
606 naming::gid_type gid = create_raw_counter(info, get_data_func, hpx::throws);
607 return naming::detail::strip_credits_from_gid(gid);
608 } // }}}
609
610 // access current counter values
get_bind_count(bool reset)611 std::int64_t symbol_namespace::counter_data::get_bind_count(bool reset)
612 {
613 return util::get_and_reset_value(bind_.count_, reset);
614 }
615
get_resolve_count(bool reset)616 std::int64_t symbol_namespace::counter_data::get_resolve_count(bool reset)
617 {
618 return util::get_and_reset_value(resolve_.count_, reset);
619 }
620
get_unbind_count(bool reset)621 std::int64_t symbol_namespace::counter_data::get_unbind_count(bool reset)
622 {
623 return util::get_and_reset_value(unbind_.count_, reset);
624 }
625
get_iterate_names_count(bool reset)626 std::int64_t symbol_namespace::counter_data::get_iterate_names_count(bool reset)
627 {
628 return util::get_and_reset_value(iterate_names_.count_, reset);
629 }
630
get_on_event_count(bool reset)631 std::int64_t symbol_namespace::counter_data::get_on_event_count(bool reset)
632 {
633 return util::get_and_reset_value(on_event_.count_, reset);
634 }
635
get_overall_count(bool reset)636 std::int64_t symbol_namespace::counter_data::get_overall_count(bool reset)
637 {
638 return util::get_and_reset_value(bind_.count_, reset) +
639 util::get_and_reset_value(resolve_.count_, reset) +
640 util::get_and_reset_value(unbind_.count_, reset) +
641 util::get_and_reset_value(iterate_names_.count_, reset) +
642 util::get_and_reset_value(on_event_.count_, reset);
643 }
644
enable_all()645 void symbol_namespace::counter_data::enable_all()
646 {
647 bind_.enabled_ = true;
648 resolve_.enabled_ = true;
649 unbind_.enabled_ = true;
650 iterate_names_.enabled_ = true;
651 on_event_.enabled_ = true;
652 }
653
654 // access execution time counters
get_bind_time(bool reset)655 std::int64_t symbol_namespace::counter_data::get_bind_time(bool reset)
656 {
657 return util::get_and_reset_value(bind_.time_, reset);
658 }
659
get_resolve_time(bool reset)660 std::int64_t symbol_namespace::counter_data::get_resolve_time(bool reset)
661 {
662 return util::get_and_reset_value(resolve_.time_, reset);
663 }
664
get_unbind_time(bool reset)665 std::int64_t symbol_namespace::counter_data::get_unbind_time(bool reset)
666 {
667 return util::get_and_reset_value(unbind_.time_, reset);
668 }
669
get_iterate_names_time(bool reset)670 std::int64_t symbol_namespace::counter_data::get_iterate_names_time(bool reset)
671 {
672 return util::get_and_reset_value(iterate_names_.time_, reset);
673 }
674
get_on_event_time(bool reset)675 std::int64_t symbol_namespace::counter_data::get_on_event_time(bool reset)
676 {
677 return util::get_and_reset_value(on_event_.time_, reset);
678 }
679
get_overall_time(bool reset)680 std::int64_t symbol_namespace::counter_data::get_overall_time(bool reset)
681 {
682 return util::get_and_reset_value(bind_.time_, reset) +
683 util::get_and_reset_value(resolve_.time_, reset) +
684 util::get_and_reset_value(unbind_.time_, reset) +
685 util::get_and_reset_value(iterate_names_.time_, reset) +
686 util::get_and_reset_value(on_event_.time_, reset);
687 }
688
689 // increment counter values
increment_bind_count()690 void symbol_namespace::counter_data::increment_bind_count()
691 {
692 if (bind_.enabled_)
693 {
694 ++bind_.count_;
695 }
696 }
697
increment_resolve_count()698 void symbol_namespace::counter_data::increment_resolve_count()
699 {
700 if (resolve_.enabled_)
701 {
702 ++resolve_.count_;
703 }
704 }
705
increment_unbind_count()706 void symbol_namespace::counter_data::increment_unbind_count()
707 {
708 if (unbind_.enabled_)
709 {
710 ++unbind_.count_;
711 }
712 }
713
increment_iterate_names_count()714 void symbol_namespace::counter_data::increment_iterate_names_count()
715 {
716 if (iterate_names_.enabled_)
717 {
718 ++iterate_names_.count_;
719 }
720 }
721
increment_on_event_count()722 void symbol_namespace::counter_data::increment_on_event_count()
723 {
724 if (on_event_.enabled_)
725 {
726 ++on_event_.count_;
727 }
728 }
729
730 }}}
731
732