semian 0.7.1 → 0.7.4

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
  SHA1:
3
- metadata.gz: 5d7a8e836a1f026b0dc5afcf4454f9949fcb44e0
4
- data.tar.gz: 68858abd9489ecb207656472ff910a7f6ad824ce
3
+ metadata.gz: a4701f3daa0b3ff3ed0f7a2a60ae025d023a4b2c
4
+ data.tar.gz: 464b2d490be5404dbabd1ed50d15b84e5bdd7e11
5
5
  SHA512:
6
- metadata.gz: 1cacd63d08f2fc3b7f42509295b3735ed8948c9185a8a236af5be737226e89dbf7614eec29f6f7d3d26cce5e05f66a6ef3a550b5081c1e0736575427702b912b
7
- data.tar.gz: f736999b06d706947b2ba81170c4fbd1f5b6d2fa909c4058d60806e918cea1bad5d6757726929feb1f8fa382d8a013f9ac6827527067dff6cb886a27c3e282e7
6
+ metadata.gz: 65122ec1d4ef4c7cb337ac00d4f8838d38b6ac8777b1e4a64d52b2381cb0b9938fa1cc85da3e6fabc88aa98c7e48cde73c253d090597fdb3ddf1b0f5f4399734
7
+ data.tar.gz: 545e6a6b3350bf4e06156945d84c5a89d2d57dc75d7e0a3dc83cbe385b8ba8a5a8585c21cc85bb9328591ecce027b0cc522772be221dcf72304f3c0769dbb402
@@ -63,7 +63,12 @@ semian_resource_acquire(int argc, VALUE *argv, VALUE self)
63
63
  }
64
64
  }
65
65
 
66
- return rb_ensure(rb_yield, self, cleanup_semian_resource_acquire, self);
66
+ VALUE wait_time = Qnil;
67
+ if (res.wait_time >= 0) {
68
+ wait_time = LONG2NUM(res.wait_time);
69
+ }
70
+
71
+ return rb_ensure(rb_yield, wait_time, cleanup_semian_resource_acquire, self);
67
72
  }
68
73
 
69
74
  VALUE
@@ -219,6 +224,7 @@ semian_resource_initialize(VALUE self, VALUE id, VALUE tickets, VALUE quota, VAL
219
224
  ms_to_timespec(c_timeout * 1000, &res->timeout);
220
225
  res->name = strdup(c_id_str);
221
226
  res->quota = c_quota;
227
+ res->wait_time = -1;
222
228
 
223
229
  // Initialize the semaphore set
224
230
  initialize_semaphore_set(res, c_id_str, c_permissions, c_tickets, c_quota);
@@ -10,6 +10,7 @@ Functions here are associated with rubyland operations.
10
10
  #include "sysv_semaphores.h"
11
11
 
12
12
  // Ruby variables
13
+ ID id_wait_time;
13
14
  ID id_timeout;
14
15
  int system_max_semaphore_count;
15
16
 
@@ -53,6 +53,7 @@ void Init_semian()
53
53
  rb_define_method(cResource, "reset_registered_workers!", semian_resource_reset_workers, 0);
54
54
  rb_define_method(cResource, "unregister_worker", semian_resource_unregister_worker, 0);
55
55
 
56
+ id_wait_time = rb_intern("wait_time");
56
57
  id_timeout = rb_intern("timeout");
57
58
 
58
59
  if (semctl(0, 0, SEM_INFO, &info_buf) == -1) {
@@ -1,4 +1,5 @@
1
1
  #include "sysv_semaphores.h"
2
+ #include <time.h>
2
3
 
3
4
  static key_t
4
5
  generate_key(const char *name);
@@ -12,6 +13,9 @@ wait_for_new_semaphore_set(key_t key, long permissions);
12
13
  static void
13
14
  initialize_new_semaphore_values(int sem_id, long permissions);
14
15
 
16
+ static long
17
+ diff_timespec_ms(struct timespec *end, struct timespec *begin);
18
+
15
19
  // Generate string rep for sem indices for debugging puproses
16
20
  static const char *SEMINDEX_STRING[] = {
17
21
  FOREACH_SEMINDEX(GENERATE_STRING)
@@ -59,9 +63,23 @@ initialize_semaphore_set(semian_resource_t* res, const char* id_str, long permis
59
63
  rb_raise(eInternal, "error incrementing registered workers, errno: %d (%s)", errno, strerror(errno));
60
64
  }
61
65
 
66
+ int state = 0;
62
67
  sem_meta_lock(res->sem_id); // Sets otime for the first time by acquiring the sem lock
63
- configure_tickets(res->sem_id, tickets, quota);
68
+
69
+ configure_tickets_args_t configure_tickets_args = (configure_tickets_args_t){
70
+ .sem_id = res->sem_id,
71
+ .tickets = tickets,
72
+ .quota = quota,
73
+ };
74
+ rb_protect(
75
+ configure_tickets,
76
+ (VALUE)&configure_tickets_args,
77
+ &state);
78
+
64
79
  sem_meta_unlock(res->sem_id);
80
+ if (state) {
81
+ rb_jump_tag(state);
82
+ }
65
83
  }
66
84
 
67
85
  void
@@ -142,12 +160,21 @@ acquire_semaphore(void *p)
142
160
  {
143
161
  semian_resource_t *res = (semian_resource_t *) p;
144
162
  res->error = 0;
163
+ res->wait_time = -1;
145
164
  #ifdef DEBUG
146
165
  print_sem_vals(res->sem_id);
147
166
  #endif
167
+
168
+ struct timespec begin, end;
169
+ int benchmark_result = clock_gettime(CLOCK_MONOTONIC, &begin);
148
170
  if (perform_semop(res->sem_id, SI_SEM_TICKETS, -1, SEM_UNDO, &res->timeout) == -1) {
149
171
  res->error = errno;
150
172
  }
173
+ if (benchmark_result == 0) {
174
+ if (clock_gettime(CLOCK_MONOTONIC, &end) == 0) {
175
+ res->wait_time = diff_timespec_ms(&end, &begin);
176
+ }
177
+ }
151
178
  return NULL;
152
179
  }
153
180
 
@@ -211,7 +238,7 @@ wait_for_new_semaphore_set(key_t key, long permissions)
211
238
  for (i = 0; i < ((INTERNAL_TIMEOUT * MICROSECONDS_IN_SECOND) / INIT_WAIT); i++) {
212
239
 
213
240
  if (semctl(sem_id, 0, IPC_STAT, sem_opts) == -1) {
214
- raise_semian_syscall_error("semget()", errno);
241
+ raise_semian_syscall_error("semctl()", errno);
215
242
  }
216
243
 
217
244
  // If a semop has been performed by someone else, the values must be initialized
@@ -231,3 +258,11 @@ wait_for_new_semaphore_set(key_t key, long permissions)
231
258
 
232
259
  return sem_id;
233
260
  }
261
+
262
+ static long
263
+ diff_timespec_ms(struct timespec *end, struct timespec *begin)
264
+ {
265
+ long end_ms = (end->tv_sec * 1e3) + (end->tv_nsec / 1e6);
266
+ long begin_ms = (begin->tv_sec * 1e3) + (begin->tv_nsec / 1e6);
267
+ return end_ms - begin_ms;
268
+ }
@@ -2,20 +2,19 @@
2
2
 
3
3
  // Update the ticket count for static ticket tracking
4
4
  static VALUE
5
- update_ticket_count(update_ticket_count_t *tc);
5
+ update_ticket_count(int sem_id, int count);
6
6
 
7
7
  static int
8
8
  calculate_quota_tickets(int sem_id, double quota);
9
9
 
10
10
  // Must be called with the semaphore meta lock already acquired
11
- void
12
- configure_tickets(int sem_id, int tickets, double quota)
11
+ VALUE
12
+ configure_tickets(VALUE value)
13
13
  {
14
- int state = 0;
15
- update_ticket_count_t tc;
14
+ configure_tickets_args_t *args = (configure_tickets_args_t *)value;
16
15
 
17
- if (quota > 0) {
18
- tickets = calculate_quota_tickets(sem_id, quota);
16
+ if (args->quota > 0) {
17
+ args->tickets = calculate_quota_tickets(args->sem_id, args->quota);
19
18
  }
20
19
 
21
20
  /*
@@ -23,10 +22,10 @@ configure_tickets(int sem_id, int tickets, double quota)
23
22
  We need to throw an error if we set it to 0 during initialization.
24
23
  Otherwise, we back out of here completely.
25
24
  */
26
- if (get_sem_val(sem_id, SI_SEM_CONFIGURED_TICKETS) == 0 && tickets == 0) {
25
+ if (get_sem_val(args->sem_id, SI_SEM_CONFIGURED_TICKETS) == 0 && args->tickets == 0) {
27
26
  rb_raise(eSyscall, "More than 0 tickets must be specified when initializing semaphore");
28
- } else if (tickets == 0) {
29
- return;
27
+ } else if (args->tickets == 0) {
28
+ return Qnil;
30
29
  }
31
30
 
32
31
  /*
@@ -34,31 +33,26 @@ configure_tickets(int sem_id, int tickets, double quota)
34
33
  count, we need to resize the count. We do this by adding the delta of
35
34
  (tickets - current_configured_tickets) to the semaphore value.
36
35
  */
37
- if (get_sem_val(sem_id, SI_SEM_CONFIGURED_TICKETS) != tickets) {
38
-
39
- tc.sem_id = sem_id;
40
- tc.tickets = tickets;
41
- rb_protect((VALUE (*)(VALUE)) update_ticket_count, (VALUE) &tc, &state);
42
-
43
- if (state) {
44
- rb_jump_tag(state);
45
- }
36
+ if (get_sem_val(args->sem_id, SI_SEM_CONFIGURED_TICKETS) != args->tickets) {
37
+ update_ticket_count(args->sem_id, args->tickets);
46
38
  }
39
+
40
+ return Qnil;
47
41
  }
48
42
 
49
43
  static VALUE
50
- update_ticket_count(update_ticket_count_t *tc)
44
+ update_ticket_count(int sem_id, int tickets)
51
45
  {
52
46
  short delta;
53
47
  struct timespec ts = { 0 };
54
48
  ts.tv_sec = INTERNAL_TIMEOUT;
55
49
 
56
- delta = tc->tickets - get_sem_val(tc->sem_id, SI_SEM_CONFIGURED_TICKETS);
50
+ delta = tickets - get_sem_val(sem_id, SI_SEM_CONFIGURED_TICKETS);
57
51
 
58
52
  #ifdef DEBUG
59
- print_sem_vals(tc->sem_id);
53
+ print_sem_vals(sem_id);
60
54
  #endif
61
- if (perform_semop(tc->sem_id, SI_SEM_TICKETS, delta, 0, &ts) == -1) {
55
+ if (perform_semop(sem_id, SI_SEM_TICKETS, delta, 0, &ts) == -1) {
62
56
  if (delta < 0 && errno == EAGAIN) {
63
57
  rb_raise(eTimeout, "timeout while trying to update ticket count");
64
58
  } else {
@@ -66,7 +60,7 @@ update_ticket_count(update_ticket_count_t *tc)
66
60
  }
67
61
  }
68
62
 
69
- if (semctl(tc->sem_id, SI_SEM_CONFIGURED_TICKETS, SETVAL, tc->tickets) == -1) {
63
+ if (semctl(sem_id, SI_SEM_CONFIGURED_TICKETS, SETVAL, tickets) == -1) {
70
64
  rb_raise(eInternal, "error configuring ticket count, errno: %d (%s)", errno, strerror(errno));
71
65
  }
72
66
 
@@ -7,7 +7,7 @@ For logic specific to manipulating semian ticket counts
7
7
  #include "sysv_semaphores.h"
8
8
 
9
9
  // Set initial ticket values upon resource creation
10
- void
11
- configure_tickets(int sem_id, int tickets, double quota);
10
+ VALUE
11
+ configure_tickets(VALUE);
12
12
 
13
13
  #endif // SEMIAN_TICKETS_H
@@ -20,11 +20,11 @@ union semun {
20
20
  (Linux-specific) */
21
21
  };
22
22
 
23
- // To update the ticket count
24
23
  typedef struct {
25
24
  int sem_id;
26
25
  int tickets;
27
- } update_ticket_count_t;
26
+ double quota;
27
+ } configure_tickets_args_t;
28
28
 
29
29
  // Internal semaphore structure
30
30
  typedef struct {
@@ -35,6 +35,7 @@ typedef struct {
35
35
  uint64_t key;
36
36
  char *strkey;
37
37
  char *name;
38
+ long wait_time;
38
39
  } semian_resource_t;
39
40
 
40
41
  #endif // SEMIAN_TYPES_H
@@ -113,7 +113,7 @@ module Semian
113
113
  str = "[#{self.class.name}] State transition from #{@state.value} to #{new_state}."
114
114
  str << " success_count=#{@successes.value} error_count=#{@errors.size}"
115
115
  str << " success_count_threshold=#{@success_count_threshold} error_count_threshold=#{@error_count_threshold}"
116
- str << " error_timeout=#{@error_timeout} error_last_at=\"#{@error_last_at}\""
116
+ str << " error_timeout=#{@error_timeout} error_last_at=\"#{@errors.last}\""
117
117
  Semian.logger.info(str)
118
118
  end
119
119
  end
@@ -43,15 +43,6 @@ module Semian
43
43
  ::SystemCallError, # includes ::Errno::EINVAL, ::Errno::ECONNRESET, ::Errno::ECONNREFUSED, ::Errno::ETIMEDOUT, and more
44
44
  ].freeze # Net::HTTP can throw many different errors, this tries to capture most of them
45
45
 
46
- # The naked methods are exposed as `raw_query` and `raw_connect` for instrumentation purpose
47
- def self.included(base)
48
- base.send(:alias_method, :raw_transport_request, :transport_request)
49
- base.send(:remove_method, :transport_request)
50
-
51
- base.send(:alias_method, :raw_connect, :connect)
52
- base.send(:remove_method, :connect)
53
- end
54
-
55
46
  class << self
56
47
  attr_accessor :exceptions
57
48
  attr_reader :semian_configuration
@@ -88,26 +79,26 @@ module Semian
88
79
  end
89
80
 
90
81
  def connect
91
- return raw_connect if disabled?
92
- acquire_semian_resource(adapter: :http, scope: :connection) { raw_connect }
82
+ return super if disabled?
83
+ acquire_semian_resource(adapter: :http, scope: :connection) { super }
93
84
  end
94
85
 
95
- def transport_request(req, &block)
96
- return raw_transport_request(req, &block) if disabled?
86
+ def transport_request(*)
87
+ return super if disabled?
97
88
  acquire_semian_resource(adapter: :http, scope: :query) do
98
- res = raw_transport_request(req, &block)
99
- handle_error_responses(res)
100
- res
89
+ handle_error_responses(super)
101
90
  end
102
91
  end
103
92
 
104
93
  private
105
94
 
106
- def handle_error_responses(res)
107
- return unless raw_semian_options.fetch(:open_circuit_server_errors, false)
108
- semian_resource.mark_failed(res) if res.is_a?(::Net::HTTPServerError)
95
+ def handle_error_responses(result)
96
+ if raw_semian_options.fetch(:open_circuit_server_errors, false)
97
+ semian_resource.mark_failed(result) if result.is_a?(::Net::HTTPServerError)
98
+ end
99
+ result
109
100
  end
110
101
  end
111
102
  end
112
103
 
113
- Net::HTTP.include(Semian::NetHTTP)
104
+ Net::HTTP.prepend(Semian::NetHTTP)
@@ -46,8 +46,8 @@ module Semian
46
46
  if @bulkhead.nil?
47
47
  yield self
48
48
  else
49
- @bulkhead.acquire(timeout: timeout) do
50
- Semian.notify(:success, self, scope, adapter)
49
+ @bulkhead.acquire(timeout: timeout) do |wait_time|
50
+ Semian.notify(:success, self, scope, adapter, wait_time)
51
51
  yield self
52
52
  end
53
53
  end
@@ -28,7 +28,8 @@ module Semian
28
28
  end
29
29
 
30
30
  def acquire(*)
31
- yield self
31
+ wait_time = 0
32
+ yield wait_time
32
33
  end
33
34
 
34
35
  def count
@@ -1,3 +1,3 @@
1
1
  module Semian
2
- VERSION = '0.7.1'
2
+ VERSION = '0.7.4'
3
3
  end
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.7.1
4
+ version: 0.7.4
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: 2017-06-23 00:00:00.000000000 Z
13
+ date: 2017-11-07 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: rake-compiler
@@ -179,7 +179,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
179
179
  version: '0'
180
180
  requirements: []
181
181
  rubyforge_project:
182
- rubygems_version: 2.5.2
182
+ rubygems_version: 2.5.2.1
183
183
  signing_key:
184
184
  specification_version: 4
185
185
  summary: Bulkheading for Ruby with SysV semaphores