semian 0.22.0.RC.1 → 0.22.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: afd87705b68966cef884eeeb1d5df4d052eeb86f2ff7d43c9499435795341461
4
- data.tar.gz: 911a0772662b81e21dcb475c9b981ceb0fbaeadf26edbbca70ab8adbf5be4068
3
+ metadata.gz: 27fa3d35b047fbe54483763821e246e2ce6f1403d8b3354d5759c4bdf1b43b2b
4
+ data.tar.gz: '06303901cc95e293dee76cd712283a4a7db1536771133e83563c969a6f3598e5'
5
5
  SHA512:
6
- metadata.gz: 8edb70d967cb15e12b919b3a7e6585f98848e2d569683d590d2d37e32eb57b65cd9f425931a3bf1575d91fc6c50e9d43901340cb33a34a671a585956b947514f
7
- data.tar.gz: b880f4f52891cbc4fd3d97221053bddc741aa72a98b986826b8ca08454c73ccbe714cff311966387f40b83e2f48a6d2db4ddf23407ff275e55bfb8c1730b5673
6
+ metadata.gz: 31e47dcea63ba9fc861272d9b8c2d591eb5f2d0783b1d8cde667812ba1cc4307ea52b533f8dc3542d011fb98c770ed011ff3a84c3e1ee7239635b01c0717b1ad
7
+ data.tar.gz: e32d7f17f6d73c95418fe6b80f1dce3423271423d61c8acd1fda356dab02aac41c24637d9a0939d39e5ade7cb015a2e47182361ea5c9187b1b39d93d59058510
data/README.md CHANGED
@@ -270,23 +270,23 @@ SEMIAN_PARAMETERS = {
270
270
  dynamic: true,
271
271
  }
272
272
 
273
- class CurrentSemianSubResource < ActiveSupport::Attributes
274
- attribute :name
273
+ class CurrentSemianSubResource < ActiveSupport::CurrentAttributes
274
+ attribute :sub_name
275
275
  end
276
276
 
277
277
  Semian::NetHTTP.semian_configuration = proc do |host, port|
278
278
  name = "#{host}_#{port}"
279
- if (sub_resource_name = CurrentSemianSubResource.name)
280
- name << "_#{name}"
279
+ if (sub_resource_name = CurrentSemianSubResource.sub_name)
280
+ name << "_#{sub_resource_name}"
281
281
  end
282
282
  SEMIAN_PARAMETERS.merge(name: name)
283
283
  end
284
284
 
285
285
  # Two requests to example.com can use two different semian resources,
286
- # as long as `CurrentSemianSubResource.name` is set accordingly:
287
- # CurrentSemianSubResource.set(name: "sub_resource_1") { Net::HTTP.get_response(URI("http://example.com")) }
286
+ # as long as `CurrentSemianSubResource.sub_name` is set accordingly:
287
+ # CurrentSemianSubResource.set(sub_name: "sub_resource_1") { Net::HTTP.get_response(URI("http://example.com")) }
288
288
  # and:
289
- # CurrentSemianSubResource.set(name: "sub_resource_2") { Net::HTTP.get_response(URI("http://example.com")) }
289
+ # CurrentSemianSubResource.set(sub_name: "sub_resource_2") { Net::HTTP.get_response(URI("http://example.com")) }
290
290
  ```
291
291
 
292
292
  For most purposes, `"#{host}_#{port}"` is a good default `name`. Custom `name` formats
@@ -5,9 +5,6 @@ ID id_wait_time;
5
5
  ID id_timeout;
6
6
  int system_max_semaphore_count;
7
7
 
8
- static VALUE
9
- cleanup_semian_resource_acquire(VALUE self);
10
-
11
8
  static void
12
9
  check_tickets_xor_quota_arg(VALUE tickets, VALUE quota);
13
10
 
@@ -33,15 +30,11 @@ static const rb_data_type_t
33
30
  semian_resource_type;
34
31
 
35
32
  VALUE
36
- semian_resource_acquire(int argc, VALUE *argv, VALUE self)
33
+ semian_resource_acquire_semaphore(int argc, VALUE *argv, VALUE self)
37
34
  {
38
35
  semian_resource_t *self_res = NULL;
39
36
  semian_resource_t res = { 0 };
40
37
 
41
- if (!rb_block_given_p()) {
42
- rb_raise(rb_eArgError, "acquire requires a block");
43
- }
44
-
45
38
  TypedData_Get_Struct(self, semian_resource_t, &semian_resource_type, self_res);
46
39
  res = *self_res;
47
40
 
@@ -75,7 +68,19 @@ semian_resource_acquire(int argc, VALUE *argv, VALUE self)
75
68
  wait_time = LONG2NUM(res.wait_time);
76
69
  }
77
70
 
78
- return rb_ensure(rb_yield, wait_time, cleanup_semian_resource_acquire, self);
71
+ return wait_time;
72
+ }
73
+
74
+ VALUE
75
+ semian_resource_acquire(int argc, VALUE *argv, VALUE self)
76
+ {
77
+ if (!rb_block_given_p()) {
78
+ rb_raise(rb_eArgError, "acquire requires a block");
79
+ }
80
+
81
+ VALUE wait_time = semian_resource_acquire_semaphore(argc, argv, self);
82
+
83
+ return rb_ensure(rb_yield, wait_time, semian_resource_release_semaphore, self);
79
84
  }
80
85
 
81
86
  VALUE
@@ -253,8 +258,8 @@ semian_resource_in_use(VALUE self)
253
258
  return Qtrue;
254
259
  }
255
260
 
256
- static VALUE
257
- cleanup_semian_resource_acquire(VALUE self)
261
+ VALUE
262
+ semian_resource_release_semaphore(VALUE self)
258
263
  {
259
264
  semian_resource_t *res = NULL;
260
265
  TypedData_Get_Struct(self, semian_resource_t, &semian_resource_type, res);
@@ -33,10 +33,39 @@ semian_resource_initialize(VALUE self, VALUE id, VALUE tickets, VALUE quota, VAL
33
33
  *
34
34
  * If no timeout argument is provided, the default timeout passed to Semian.register will be used.
35
35
  *
36
+ * The given block is executed with the semaphore held and, when the block
37
+ * exits, the semaphore is automatically released.
36
38
  */
37
39
  VALUE
38
40
  semian_resource_acquire(int argc, VALUE *argv, VALUE self);
39
41
 
42
+ /*
43
+ * call-seq:
44
+ * resource.acquire_semaphore(timeout: default_timeout) -> wait_time
45
+ *
46
+ * Acquires a resource. The call will block for <code>timeout</code> seconds if a ticket
47
+ * is not available. If no ticket is available within the timeout period, Semian::TimeoutError
48
+ * will be raised.
49
+ *
50
+ * If no timeout argument is provided, the default timeout passed to Semian.register will be used.
51
+ *
52
+ * Note: The caller is responsible for releasing the semaphore when done by calling release_semaphore.
53
+ */
54
+ VALUE
55
+ semian_resource_acquire_semaphore(int argc, VALUE *argv, VALUE self);
56
+
57
+ /*
58
+ * call-seq:
59
+ * resource.release_semaphore() -> nil
60
+ *
61
+ * Releases a resource previously acquired with acquire_semaphore.
62
+ *
63
+ * Note: The method is NOT idempotent. The caller must ensure that the method is called exactly
64
+ * as many times as acquire_semaphore.
65
+ */
66
+ VALUE
67
+ semian_resource_release_semaphore(VALUE self);
68
+
40
69
  /*
41
70
  * call-seq:
42
71
  * resource.destroy() -> true
data/ext/semian/semian.c CHANGED
@@ -60,6 +60,8 @@ void Init_semian()
60
60
  rb_define_alloc_func(cResource, semian_resource_alloc);
61
61
  rb_define_method(cResource, "initialize_semaphore", semian_resource_initialize, 5);
62
62
  rb_define_method(cResource, "acquire", semian_resource_acquire, -1);
63
+ rb_define_method(cResource, "acquire_semaphore", semian_resource_acquire_semaphore, -1);
64
+ rb_define_method(cResource, "release_semaphore", semian_resource_release_semaphore, 0);
63
65
  rb_define_method(cResource, "count", semian_resource_count, 0);
64
66
  rb_define_method(cResource, "semid", semian_resource_id, 0);
65
67
  rb_define_method(cResource, "key", semian_resource_key, 0);
@@ -4,7 +4,7 @@ module Semian
4
4
  class ProtectedResource
5
5
  extend Forwardable
6
6
 
7
- def_delegators :@bulkhead, :destroy, :count, :semid, :tickets, :registered_workers
7
+ def_delegators :@bulkhead, :count, :semid, :tickets, :registered_workers
8
8
  def_delegators :@circuit_breaker,
9
9
  :reset,
10
10
  :mark_failed,
@@ -9,6 +9,12 @@ module Semian
9
9
  def instance(name, **kwargs)
10
10
  Semian.resources[name] ||= ProtectedResource.new(name, new(name, **kwargs), nil)
11
11
  end
12
+
13
+ private
14
+
15
+ def redefinable(method_name)
16
+ alias_method(method_name, method_name) # Silence method redefinition warnings
17
+ end
12
18
  end
13
19
 
14
20
  def initialize(name, tickets: nil, quota: nil, permissions: Semian.default_permissions, timeout: 0)
@@ -26,41 +32,48 @@ module Semian
26
32
  @name = name
27
33
  end
28
34
 
29
- def reset_registered_workers!
35
+ redefinable def reset_registered_workers!
30
36
  end
31
37
 
32
- def destroy
38
+ redefinable def destroy
33
39
  end
34
40
 
35
- def unregister_worker
41
+ redefinable def unregister_worker
36
42
  end
37
43
 
38
- def acquire(*)
44
+ redefinable def acquire(*)
39
45
  wait_time = 0
40
46
  yield wait_time
41
47
  end
42
48
 
43
- def count
49
+ redefinable def acquire_semaphore
50
+ 0
51
+ end
52
+
53
+ redefinable def release_semaphore
54
+ end
55
+
56
+ redefinable def count
44
57
  0
45
58
  end
46
59
 
47
- def tickets
60
+ redefinable def tickets
48
61
  0
49
62
  end
50
63
 
51
- def registered_workers
64
+ redefinable def registered_workers
52
65
  0
53
66
  end
54
67
 
55
- def semid
68
+ redefinable def semid
56
69
  0
57
70
  end
58
71
 
59
- def key
72
+ redefinable def key
60
73
  "0x00000000"
61
74
  end
62
75
 
63
- def in_use?
76
+ redefinable def in_use?
64
77
  false
65
78
  end
66
79
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Semian
4
- VERSION = "0.22.0.RC.1"
4
+ VERSION = "0.22.0"
5
5
  end
data/lib/semian.rb CHANGED
@@ -275,6 +275,21 @@ module Semian
275
275
  @thread_safe = thread_safe
276
276
  end
277
277
 
278
+ THREAD_BULKHEAD_DISABLED_VAR = :semian_bulkheads_disabled
279
+ private_constant(:THREAD_BULKHEAD_DISABLED_VAR)
280
+
281
+ def bulkheads_disabled_in_thread?(thread)
282
+ thread.thread_variable_get(THREAD_BULKHEAD_DISABLED_VAR)
283
+ end
284
+
285
+ def disable_bulkheads_for_thread(thread)
286
+ old_value = thread.thread_variable_get(THREAD_BULKHEAD_DISABLED_VAR)
287
+ thread.thread_variable_set(THREAD_BULKHEAD_DISABLED_VAR, true)
288
+ yield
289
+ ensure
290
+ thread.thread_variable_set(THREAD_BULKHEAD_DISABLED_VAR, old_value)
291
+ end
292
+
278
293
  private
279
294
 
280
295
  def create_circuit_breaker(name, **options)
@@ -318,7 +333,7 @@ module Semian
318
333
  end
319
334
 
320
335
  def create_bulkhead(name, **options)
321
- return if ENV.key?("SEMIAN_BULKHEAD_DISABLED")
336
+ return if ENV.key?("SEMIAN_BULKHEAD_DISABLED") || bulkheads_disabled_in_thread?(Thread.current)
322
337
  return unless options.fetch(:bulkhead, true)
323
338
 
324
339
  permissions = options[:permissions] || default_permissions
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: semian
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.22.0.RC.1
4
+ version: 0.22.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Scott Francis
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2024-07-02 00:00:00.000000000 Z
13
+ date: 2024-11-05 00:00:00.000000000 Z
14
14
  dependencies: []
15
15
  description: |2
16
16
  A Ruby C extention that is used to control access to shared resources
@@ -79,7 +79,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
79
79
  - !ruby/object:Gem::Version
80
80
  version: '0'
81
81
  requirements: []
82
- rubygems_version: 3.5.14
82
+ rubygems_version: 3.5.22
83
83
  signing_key:
84
84
  specification_version: 4
85
85
  summary: Bulkheading for Ruby with SysV semaphores