atomic-ruby 0.15.1 → 0.15.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: af7a35affcaa2790e6944ee01eb6af8f4d92b5c2479bb1995128742a562fd395
4
- data.tar.gz: d3908277e6ef09a70f7d46a057a5db7f60c179885d980b4fbbf407bdade6b073
3
+ metadata.gz: 9c6210195ad6d82fbbb6a84f66bc8ce5faef54d50ed3227c3655ec43a996a9f6
4
+ data.tar.gz: eef94abbb8c8bfb1d9a54d36f8f8b0404d49210e6e6a109851fa014a6512aaa5
5
5
  SHA512:
6
- metadata.gz: f5a3525b194636ddb439c0c8e9597e3ed1870a8c0165bc1582cb120a45c49b30a8cf6bd60f393a9e3e0c848abf760b181dce1b9b3026199ff8ffb2ba833dcc1a
7
- data.tar.gz: e89a1f593774fc1722bfc8a9229385682e33a10df705ba78da3c67f904ab566c770b7aebdebc32069ed7f4dfc2781ad52165f08f10cf3fd4e4d723c41687e02f
6
+ metadata.gz: c89b7c62bda07206115c80c45088d1ae44e5c3aa84288932fae8fad51312d3490bf9510af559178fe6311e5d8ce4ecf87dca0da3420ced47a0113783e63a741d
7
+ data.tar.gz: a5c5ecee1ce3f96b5e2c525897c1c979fa3f99b7be6cd971d6882e2a43bf9b735f0de58bcf67dbf1179b0298a006a25ba45e910ee0c104f965c305e4af93f6be
data/CHANGELOG.md CHANGED
@@ -1,5 +1,9 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.15.2] - 2026-08-30
4
+
5
+ - Avoid thread event hook overhead for fixed thread pools
6
+
3
7
  ## [0.15.1] - 2026-08-30
4
8
 
5
9
  - Prevent OS scheduling delays from stopping adaptive thread pool growth
data/README.md CHANGED
@@ -268,7 +268,7 @@ puts "Atomic Ruby Atomic Bank Account: #{results[2].real.round(6)} seconds"
268
268
 
269
269
  ruby version: ruby 4.0.6 (2026-07-14 revision 03b6d3f889) +YJIT +PRISM [arm64-darwin23]
270
270
  concurrent-ruby version: 1.3.6
271
- atomic-ruby version: 0.15.1
271
+ atomic-ruby version: 0.15.2
272
272
 
273
273
  Balances:
274
274
  Synchronized Bank Account Balance: 975
@@ -359,7 +359,7 @@ end
359
359
 
360
360
  ruby version: ruby 4.0.6 (2026-07-14 revision 03b6d3f889) +YJIT +PRISM [arm64-darwin23]
361
361
  concurrent-ruby version: 1.3.6
362
- atomic-ruby version: 0.15.1
362
+ atomic-ruby version: 0.15.2
363
363
 
364
364
  Warming up --------------------------------------
365
365
  Synchronized Boolean Toggle 157.000 i/100ms
@@ -442,7 +442,7 @@ end
442
442
  > bundle exec rake clobber && bundle exec rake compile && bundle exec ruby examples/atomic_condition_variable_benchmark.rb
443
443
 
444
444
  ruby version: ruby 4.0.6 (2026-07-14 revision 03b6d3f889) +YJIT +PRISM [arm64-darwin23]
445
- atomic-ruby version: 0.15.1
445
+ atomic-ruby version: 0.15.2
446
446
 
447
447
  Warming up --------------------------------------
448
448
  Synchronized Condition Variable Wait/Signal 3.977k i/100ms
@@ -517,7 +517,7 @@ end
517
517
  > bundle exec rake clobber && bundle exec rake compile && bundle exec ruby examples/atomic_queue_benchmark.rb
518
518
 
519
519
  ruby version: ruby 4.0.6 (2026-07-14 revision 03b6d3f889) +YJIT +PRISM [arm64-darwin23]
520
- atomic-ruby version: 0.15.1
520
+ atomic-ruby version: 0.15.2
521
521
 
522
522
  Warming up --------------------------------------
523
523
  Synchronized Queue Push/Pop 181.000 i/100ms
@@ -587,11 +587,11 @@ puts "Atomic Ruby Atomic Thread Pool: #{results[1].real.round(6)} seconds"
587
587
 
588
588
  ruby version: ruby 4.0.6 (2026-07-14 revision 03b6d3f889) +YJIT +PRISM [arm64-darwin23]
589
589
  concurrent-ruby version: 1.3.8
590
- atomic-ruby version: 0.15.1
590
+ atomic-ruby version: 0.15.2
591
591
 
592
592
  Benchmark Results:
593
- Concurrent Ruby Thread Pool: 5.079561 seconds
594
- Atomic Ruby Atomic Thread Pool: 4.99168 seconds
593
+ Concurrent Ruby Thread Pool: 5.081233 seconds
594
+ Atomic Ruby Atomic Thread Pool: 4.737824 seconds
595
595
  ```
596
596
 
597
597
  </details>
@@ -536,6 +536,7 @@ typedef struct {
536
536
  _Atomic unsigned long long waiting_time;
537
537
  _Atomic unsigned long long blocked_time;
538
538
  _Atomic unsigned long long running_cpu_time;
539
+ bool active;
539
540
  } atomic_ruby_thread_pool_monitor_t;
540
541
 
541
542
  typedef struct {
@@ -546,8 +547,27 @@ typedef struct {
546
547
  } atomic_ruby_thread_pool_worker_state_t;
547
548
 
548
549
  static rb_internal_thread_specific_key_t atomic_ruby_thread_pool_worker_key;
550
+ static rb_nativethread_lock_t atomic_ruby_thread_pool_monitor_lock;
551
+ static unsigned int atomic_ruby_thread_pool_monitor_count;
552
+ static rb_internal_thread_event_hook_t *atomic_ruby_thread_pool_event_hook;
553
+
554
+ static void atomic_ruby_thread_pool_event_callback(rb_event_flag_t event, const rb_internal_thread_event_data_t *event_data, void *user_data);
555
+
556
+ static void atomic_ruby_thread_pool_monitor_stop(atomic_ruby_thread_pool_monitor_t *monitor) {
557
+ rb_nativethread_lock_lock(&atomic_ruby_thread_pool_monitor_lock);
558
+ if (monitor->active) {
559
+ monitor->active = false;
560
+ atomic_ruby_thread_pool_monitor_count--;
561
+ if (atomic_ruby_thread_pool_monitor_count == 0) {
562
+ rb_internal_thread_remove_event_hook(atomic_ruby_thread_pool_event_hook);
563
+ atomic_ruby_thread_pool_event_hook = NULL;
564
+ }
565
+ }
566
+ rb_nativethread_lock_unlock(&atomic_ruby_thread_pool_monitor_lock);
567
+ }
549
568
 
550
569
  static void atomic_ruby_thread_pool_monitor_free(void *ptr) {
570
+ atomic_ruby_thread_pool_monitor_stop(ptr);
551
571
  xfree(ptr);
552
572
  }
553
573
 
@@ -655,9 +675,37 @@ static VALUE rb_cThreadPoolMonitor_allocate(VALUE klass) {
655
675
  atomic_init(&monitor->waiting_time, 0);
656
676
  atomic_init(&monitor->blocked_time, 0);
657
677
  atomic_init(&monitor->running_cpu_time, 0);
678
+ monitor->active = false;
658
679
  return obj;
659
680
  }
660
681
 
682
+ static VALUE rb_cThreadPoolMonitor_start(VALUE self) {
683
+ atomic_ruby_thread_pool_monitor_t *monitor;
684
+ TypedData_Get_Struct(self, atomic_ruby_thread_pool_monitor_t, &atomic_ruby_thread_pool_monitor_type, monitor);
685
+ rb_nativethread_lock_lock(&atomic_ruby_thread_pool_monitor_lock);
686
+ if (!monitor->active) {
687
+ monitor->active = true;
688
+ if (atomic_ruby_thread_pool_monitor_count++ == 0) {
689
+ atomic_ruby_thread_pool_event_hook = rb_internal_thread_add_event_hook(
690
+ atomic_ruby_thread_pool_event_callback,
691
+ RUBY_INTERNAL_THREAD_EVENT_READY |
692
+ RUBY_INTERNAL_THREAD_EVENT_RESUMED |
693
+ RUBY_INTERNAL_THREAD_EVENT_SUSPENDED,
694
+ NULL
695
+ );
696
+ }
697
+ }
698
+ rb_nativethread_lock_unlock(&atomic_ruby_thread_pool_monitor_lock);
699
+ return Qnil;
700
+ }
701
+
702
+ static VALUE rb_cThreadPoolMonitor_stop(VALUE self) {
703
+ atomic_ruby_thread_pool_monitor_t *monitor;
704
+ TypedData_Get_Struct(self, atomic_ruby_thread_pool_monitor_t, &atomic_ruby_thread_pool_monitor_type, monitor);
705
+ atomic_ruby_thread_pool_monitor_stop(monitor);
706
+ return Qnil;
707
+ }
708
+
661
709
  static VALUE rb_cThreadPoolMonitor_register_worker(VALUE self) {
662
710
  atomic_ruby_thread_pool_monitor_t *monitor;
663
711
  TypedData_Get_Struct(self, atomic_ruby_thread_pool_monitor_t, &atomic_ruby_thread_pool_monitor_type, monitor);
@@ -756,15 +804,13 @@ RUBY_FUNC_EXPORTED void Init_atomic_ruby(void) {
756
804
  rb_define_private_method(rb_cAtomicConditionVariable, "_waiter_count", rb_cAtomicConditionVariable_waiter_count, 0);
757
805
 
758
806
  atomic_ruby_thread_pool_worker_key = rb_internal_thread_specific_key_create();
759
- rb_internal_thread_add_event_hook(
760
- atomic_ruby_thread_pool_event_callback,
761
- RUBY_INTERNAL_THREAD_EVENT_READY |
762
- RUBY_INTERNAL_THREAD_EVENT_RESUMED |
763
- RUBY_INTERNAL_THREAD_EVENT_SUSPENDED,
764
- NULL
765
- );
807
+ rb_nativethread_lock_initialize(&atomic_ruby_thread_pool_monitor_lock);
808
+ atomic_ruby_thread_pool_monitor_count = 0;
809
+ atomic_ruby_thread_pool_event_hook = NULL;
766
810
  VALUE rb_cThreadPoolMonitor = rb_define_class_under(rb_mAtomicRuby, "ThreadPoolMonitor", rb_cObject);
767
811
  rb_define_alloc_func(rb_cThreadPoolMonitor, rb_cThreadPoolMonitor_allocate);
812
+ rb_define_method(rb_cThreadPoolMonitor, "start", rb_cThreadPoolMonitor_start, 0);
813
+ rb_define_method(rb_cThreadPoolMonitor, "stop", rb_cThreadPoolMonitor_stop, 0);
768
814
  rb_define_method(rb_cThreadPoolMonitor, "register_worker", rb_cThreadPoolMonitor_register_worker, 0);
769
815
  rb_define_method(rb_cThreadPoolMonitor, "unregister_worker", rb_cThreadPoolMonitor_unregister_worker, 0);
770
816
  rb_define_method(rb_cThreadPoolMonitor, "start_work", rb_cThreadPoolMonitor_start_work, 0);
@@ -4,6 +4,7 @@
4
4
  #include "ruby.h"
5
5
  #include "ruby/atomic.h"
6
6
  #include "ruby/thread.h"
7
+ #include "ruby/thread_native.h"
7
8
  #include "ruby/version.h"
8
9
 
9
10
  #include <stdatomic.h>
@@ -125,6 +125,7 @@ module AtomicRuby
125
125
  @autoscale_available = AtomicConditionVariable.new
126
126
  @trim_requested = AtomicBoolean.new(false)
127
127
  @thread_pool_monitor = ThreadPoolMonitor.new
128
+ @thread_pool_monitor.start
128
129
  end
129
130
 
130
131
  start
@@ -264,6 +265,7 @@ module AtomicRuby
264
265
  @work_available.broadcast
265
266
  @autoscaler&.join
266
267
  @threads.each(&:join)
268
+ @thread_pool_monitor&.stop
267
269
  end
268
270
 
269
271
  private
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module AtomicRuby
4
- VERSION = "0.15.1"
4
+ VERSION = "0.15.2"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: atomic-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.15.1
4
+ version: 0.15.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Joshua Young