garcun 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitattributes +17 -0
- data/.gitignore +197 -0
- data/.rspec +2 -0
- data/Gemfile +22 -0
- data/LICENSE +201 -0
- data/README.md +521 -0
- data/Rakefile +47 -0
- data/garcun.gemspec +83 -0
- data/lib/garcon.rb +290 -0
- data/lib/garcon/chef/chef_helpers.rb +343 -0
- data/lib/garcon/chef/coerce/coercer.rb +134 -0
- data/lib/garcon/chef/coerce/coercions/boolean_definitions.rb +34 -0
- data/lib/garcon/chef/coerce/coercions/date_definitions.rb +32 -0
- data/lib/garcon/chef/coerce/coercions/date_time_definitions.rb +32 -0
- data/lib/garcon/chef/coerce/coercions/fixnum_definitions.rb +34 -0
- data/lib/garcon/chef/coerce/coercions/float_definitions.rb +32 -0
- data/lib/garcon/chef/coerce/coercions/hash_definitions.rb +29 -0
- data/lib/garcon/chef/coerce/coercions/integer_definitions.rb +31 -0
- data/lib/garcon/chef/coerce/coercions/string_definitions.rb +45 -0
- data/lib/garcon/chef/coerce/coercions/time_definitions.rb +32 -0
- data/lib/garcon/chef/handler/devreporter.rb +127 -0
- data/lib/garcon/chef/log.rb +64 -0
- data/lib/garcon/chef/node.rb +100 -0
- data/lib/garcon/chef/provider/civilize.rb +209 -0
- data/lib/garcon/chef/provider/development.rb +159 -0
- data/lib/garcon/chef/provider/download.rb +420 -0
- data/lib/garcon/chef/provider/house_keeping.rb +265 -0
- data/lib/garcon/chef/provider/node_cache.rb +31 -0
- data/lib/garcon/chef/provider/partial.rb +183 -0
- data/lib/garcon/chef/provider/recovery.rb +80 -0
- data/lib/garcon/chef/provider/zip_file.rb +271 -0
- data/lib/garcon/chef/resource/attribute.rb +52 -0
- data/lib/garcon/chef/resource/base_dsl.rb +174 -0
- data/lib/garcon/chef/resource/blender.rb +140 -0
- data/lib/garcon/chef/resource/lazy_eval.rb +66 -0
- data/lib/garcon/chef/resource/resource_name.rb +109 -0
- data/lib/garcon/chef/secret_bag.rb +204 -0
- data/lib/garcon/chef/validations.rb +76 -0
- data/lib/garcon/chef_inclusions.rb +151 -0
- data/lib/garcon/configuration.rb +138 -0
- data/lib/garcon/core_ext.rb +39 -0
- data/lib/garcon/core_ext/array.rb +27 -0
- data/lib/garcon/core_ext/binding.rb +64 -0
- data/lib/garcon/core_ext/boolean.rb +66 -0
- data/lib/garcon/core_ext/duration.rb +271 -0
- data/lib/garcon/core_ext/enumerable.rb +34 -0
- data/lib/garcon/core_ext/file.rb +127 -0
- data/lib/garcon/core_ext/filetest.rb +62 -0
- data/lib/garcon/core_ext/hash.rb +279 -0
- data/lib/garcon/core_ext/kernel.rb +159 -0
- data/lib/garcon/core_ext/lazy.rb +222 -0
- data/lib/garcon/core_ext/method_access.rb +243 -0
- data/lib/garcon/core_ext/module.rb +92 -0
- data/lib/garcon/core_ext/nil.rb +53 -0
- data/lib/garcon/core_ext/numeric.rb +44 -0
- data/lib/garcon/core_ext/object.rb +342 -0
- data/lib/garcon/core_ext/pathname.rb +152 -0
- data/lib/garcon/core_ext/process.rb +41 -0
- data/lib/garcon/core_ext/random.rb +497 -0
- data/lib/garcon/core_ext/string.rb +312 -0
- data/lib/garcon/core_ext/struct.rb +49 -0
- data/lib/garcon/core_ext/symbol.rb +170 -0
- data/lib/garcon/core_ext/time.rb +234 -0
- data/lib/garcon/exceptions.rb +101 -0
- data/lib/garcon/inflections.rb +237 -0
- data/lib/garcon/inflections/defaults.rb +79 -0
- data/lib/garcon/inflections/inflections.rb +182 -0
- data/lib/garcon/inflections/rules_collection.rb +37 -0
- data/lib/garcon/secret.rb +271 -0
- data/lib/garcon/stash/format.rb +114 -0
- data/lib/garcon/stash/journal.rb +226 -0
- data/lib/garcon/stash/queue.rb +83 -0
- data/lib/garcon/stash/serializer.rb +86 -0
- data/lib/garcon/stash/store.rb +435 -0
- data/lib/garcon/task.rb +31 -0
- data/lib/garcon/task/atomic.rb +151 -0
- data/lib/garcon/task/atomic_boolean.rb +127 -0
- data/lib/garcon/task/condition.rb +99 -0
- data/lib/garcon/task/copy_on_notify_observer_set.rb +154 -0
- data/lib/garcon/task/copy_on_write_observer_set.rb +153 -0
- data/lib/garcon/task/count_down_latch.rb +92 -0
- data/lib/garcon/task/delay.rb +196 -0
- data/lib/garcon/task/dereferenceable.rb +144 -0
- data/lib/garcon/task/event.rb +119 -0
- data/lib/garcon/task/executor.rb +275 -0
- data/lib/garcon/task/executor_options.rb +59 -0
- data/lib/garcon/task/future.rb +107 -0
- data/lib/garcon/task/immediate_executor.rb +84 -0
- data/lib/garcon/task/ivar.rb +171 -0
- data/lib/garcon/task/lazy_reference.rb +74 -0
- data/lib/garcon/task/monotonic_time.rb +69 -0
- data/lib/garcon/task/obligation.rb +256 -0
- data/lib/garcon/task/observable.rb +101 -0
- data/lib/garcon/task/priority_queue.rb +234 -0
- data/lib/garcon/task/processor_count.rb +128 -0
- data/lib/garcon/task/read_write_lock.rb +304 -0
- data/lib/garcon/task/safe_task_executor.rb +58 -0
- data/lib/garcon/task/single_thread_executor.rb +97 -0
- data/lib/garcon/task/thread_pool/cached.rb +71 -0
- data/lib/garcon/task/thread_pool/executor.rb +294 -0
- data/lib/garcon/task/thread_pool/fixed.rb +61 -0
- data/lib/garcon/task/thread_pool/worker.rb +90 -0
- data/lib/garcon/task/timer.rb +44 -0
- data/lib/garcon/task/timer_set.rb +194 -0
- data/lib/garcon/task/timer_task.rb +377 -0
- data/lib/garcon/task/waitable_list.rb +58 -0
- data/lib/garcon/utility/ansi.rb +199 -0
- data/lib/garcon/utility/at_random.rb +77 -0
- data/lib/garcon/utility/crypto.rb +292 -0
- data/lib/garcon/utility/equalizer.rb +146 -0
- data/lib/garcon/utility/faker/extensions/array.rb +22 -0
- data/lib/garcon/utility/faker/extensions/symbol.rb +9 -0
- data/lib/garcon/utility/faker/faker.rb +164 -0
- data/lib/garcon/utility/faker/faker/company.rb +17 -0
- data/lib/garcon/utility/faker/faker/hacker.rb +30 -0
- data/lib/garcon/utility/faker/faker/version.rb +3 -0
- data/lib/garcon/utility/faker/locales/en-US.yml +83 -0
- data/lib/garcon/utility/faker/locales/en.yml +21 -0
- data/lib/garcon/utility/file_helper.rb +170 -0
- data/lib/garcon/utility/hookers.rb +178 -0
- data/lib/garcon/utility/interpolation.rb +90 -0
- data/lib/garcon/utility/memstash.rb +364 -0
- data/lib/garcon/utility/misc.rb +54 -0
- data/lib/garcon/utility/msg_from_god.rb +62 -0
- data/lib/garcon/utility/retry.rb +238 -0
- data/lib/garcon/utility/timeout.rb +58 -0
- data/lib/garcon/utility/uber/builder.rb +91 -0
- data/lib/garcon/utility/uber/callable.rb +7 -0
- data/lib/garcon/utility/uber/delegates.rb +13 -0
- data/lib/garcon/utility/uber/inheritable_attr.rb +37 -0
- data/lib/garcon/utility/uber/options.rb +101 -0
- data/lib/garcon/utility/uber/uber_version.rb +3 -0
- data/lib/garcon/utility/uber/version.rb +33 -0
- data/lib/garcon/utility/url_helper.rb +100 -0
- data/lib/garcon/utils.rb +29 -0
- data/lib/garcon/version.rb +62 -0
- data/lib/garcun.rb +24 -0
- metadata +680 -0
@@ -0,0 +1,153 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
#
|
3
|
+
# Author: Stefano Harding <riddopic@gmail.com>
|
4
|
+
# License: Apache License, Version 2.0
|
5
|
+
# Copyright: (C) 2014-2015 Stefano Harding
|
6
|
+
#
|
7
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
8
|
+
# you may not use this file except in compliance with the License.
|
9
|
+
# You may obtain a copy of the License at
|
10
|
+
#
|
11
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
12
|
+
#
|
13
|
+
# Unless required by applicable law or agreed to in writing, software
|
14
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
15
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
16
|
+
# See the License for the specific language governing permissions and
|
17
|
+
# limitations under the License.
|
18
|
+
#
|
19
|
+
|
20
|
+
module Garcon
|
21
|
+
|
22
|
+
# A thread safe observer set implemented using copy-on-write approach:
|
23
|
+
# every time an observer is added or removed the whole internal data structure
|
24
|
+
# is duplicated and replaced with a new one.
|
25
|
+
#
|
26
|
+
class CopyOnWriteObserverSet
|
27
|
+
|
28
|
+
def initialize
|
29
|
+
@mutex = Mutex.new
|
30
|
+
@observers = {}
|
31
|
+
end
|
32
|
+
|
33
|
+
# Adds an observer to this set. If a block is passed, the observer will be
|
34
|
+
# created by this method and no other params should be passed
|
35
|
+
#
|
36
|
+
# @param [Object] observer
|
37
|
+
# the observer to add
|
38
|
+
#
|
39
|
+
# @param [Symbol] func
|
40
|
+
# the function to call on the observer during notification. The default
|
41
|
+
# is :update.
|
42
|
+
#
|
43
|
+
# @return [Object]
|
44
|
+
# the added observer
|
45
|
+
def add_observer(observer=nil, func=:update, &block)
|
46
|
+
if observer.nil? && block.nil?
|
47
|
+
raise ArgumentError, 'should pass observer as a first argument or block'
|
48
|
+
elsif observer && block
|
49
|
+
raise ArgumentError.new('cannot provide both an observer and a block')
|
50
|
+
end
|
51
|
+
|
52
|
+
if block
|
53
|
+
observer = block
|
54
|
+
func = :call
|
55
|
+
end
|
56
|
+
|
57
|
+
begin
|
58
|
+
@mutex.lock
|
59
|
+
new_observers = @observers.dup
|
60
|
+
new_observers[observer] = func
|
61
|
+
@observers = new_observers
|
62
|
+
observer
|
63
|
+
ensure
|
64
|
+
@mutex.unlock
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# @param [Object] observer
|
69
|
+
# the observer to remove
|
70
|
+
# @return [Object]
|
71
|
+
# the deleted observer
|
72
|
+
def delete_observer(observer)
|
73
|
+
@mutex.lock
|
74
|
+
new_observers = @observers.dup
|
75
|
+
new_observers.delete(observer)
|
76
|
+
@observers = new_observers
|
77
|
+
observer
|
78
|
+
ensure
|
79
|
+
@mutex.unlock
|
80
|
+
end
|
81
|
+
|
82
|
+
# Deletes all observers
|
83
|
+
# @return [CopyOnWriteObserverSet] self
|
84
|
+
def delete_observers
|
85
|
+
self.observers = {}
|
86
|
+
self
|
87
|
+
end
|
88
|
+
|
89
|
+
|
90
|
+
# @return [Integer] the observers count
|
91
|
+
def count_observers
|
92
|
+
observers.count
|
93
|
+
end
|
94
|
+
|
95
|
+
# Notifies all registered observers with optional args
|
96
|
+
#
|
97
|
+
# @param [Object] args
|
98
|
+
# Arguments to be passed to each observer
|
99
|
+
#
|
100
|
+
# @return [CopyOnWriteObserverSet] self
|
101
|
+
def notify_observers(*args, &block)
|
102
|
+
notify_to(observers, *args, &block)
|
103
|
+
self
|
104
|
+
end
|
105
|
+
|
106
|
+
# Notifies all registered observers with optional args and deletes them.
|
107
|
+
#
|
108
|
+
# @param [Object] args
|
109
|
+
# Arguments to be passed to each observer
|
110
|
+
#
|
111
|
+
# @return [CopyOnWriteObserverSet] self
|
112
|
+
def notify_and_delete_observers(*args, &block)
|
113
|
+
old = clear_observers_and_return_old
|
114
|
+
notify_to(old, *args, &block)
|
115
|
+
self
|
116
|
+
end
|
117
|
+
|
118
|
+
private # P R O P R I E T À P R I V A T A Vietato L'accesso
|
119
|
+
|
120
|
+
def notify_to(observers, *args)
|
121
|
+
if block_given? && !args.empty?
|
122
|
+
raise ArgumentError.new 'cannot give arguments and a block'
|
123
|
+
end
|
124
|
+
observers.each do |observer, function|
|
125
|
+
args = yield if block_given?
|
126
|
+
observer.send(function, *args)
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
def observers
|
131
|
+
@mutex.lock
|
132
|
+
@observers
|
133
|
+
ensure
|
134
|
+
@mutex.unlock
|
135
|
+
end
|
136
|
+
|
137
|
+
def observers=(new_set)
|
138
|
+
@mutex.lock
|
139
|
+
@observers = new_set
|
140
|
+
ensure
|
141
|
+
@mutex.unlock
|
142
|
+
end
|
143
|
+
|
144
|
+
def clear_observers_and_return_old
|
145
|
+
@mutex.lock
|
146
|
+
old_observers = @observers
|
147
|
+
@observers = {}
|
148
|
+
old_observers
|
149
|
+
ensure
|
150
|
+
@mutex.unlock
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
#
|
3
|
+
# Author: Stefano Harding <riddopic@gmail.com>
|
4
|
+
# License: Apache License, Version 2.0
|
5
|
+
# Copyright: (C) 2014-2015 Stefano Harding
|
6
|
+
#
|
7
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
8
|
+
# you may not use this file except in compliance with the License.
|
9
|
+
# You may obtain a copy of the License at
|
10
|
+
#
|
11
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
12
|
+
#
|
13
|
+
# Unless required by applicable law or agreed to in writing, software
|
14
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
15
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
16
|
+
# See the License for the specific language governing permissions and
|
17
|
+
# limitations under the License.
|
18
|
+
#
|
19
|
+
|
20
|
+
require_relative 'condition'
|
21
|
+
|
22
|
+
module Garcon
|
23
|
+
|
24
|
+
# A synchronization object that allows one thread to wait on multiple other
|
25
|
+
# threads. The thread that will wait creates a `CountDownLatch` and sets the
|
26
|
+
# initial value (normally equal to the number of other threads). The
|
27
|
+
# initiating thread passes the latch to the other threads then waits for the
|
28
|
+
# other threads by calling the `#wait` method. Each of the other threads
|
29
|
+
# calls `#count_down` when done with its work. When the latch counter reaches
|
30
|
+
# zero the waiting thread is unblocked and continues with its work. A
|
31
|
+
# `CountDownLatch` can be used only once. Its value cannot be reset.
|
32
|
+
#
|
33
|
+
class MutexCountDownLatch
|
34
|
+
|
35
|
+
# Create a new `CountDownLatch` with the initial `count`.
|
36
|
+
#
|
37
|
+
# @param [Fixnum] count
|
38
|
+
# The initial count
|
39
|
+
#
|
40
|
+
# @raise [ArgumentError]
|
41
|
+
# If `count` is not an integer or is less than zero.
|
42
|
+
#
|
43
|
+
def initialize(count = 1)
|
44
|
+
unless count.is_a?(Fixnum) && count >= 0
|
45
|
+
raise ArgumentError, 'count must be greater than or equal zero'
|
46
|
+
end
|
47
|
+
@mutex = Mutex.new
|
48
|
+
@condition = Condition.new
|
49
|
+
@count = count
|
50
|
+
end
|
51
|
+
|
52
|
+
# Block on the latch until the counter reaches zero or until `timeout` is
|
53
|
+
# reached.
|
54
|
+
#
|
55
|
+
# @param [Fixnum] timeout
|
56
|
+
# The number of seconds to wait for the counter or `nil` to block
|
57
|
+
# indefinitely.
|
58
|
+
# @return [Boolean]
|
59
|
+
# True if the count reaches zero else false on timeout.
|
60
|
+
#
|
61
|
+
def wait(timeout = nil)
|
62
|
+
@mutex.synchronize do
|
63
|
+
remaining = Condition::Result.new(timeout)
|
64
|
+
while @count > 0 && remaining.can_wait?
|
65
|
+
remaining = @condition.wait(@mutex, remaining.remaining_time)
|
66
|
+
end
|
67
|
+
@count == 0
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
# Signal the latch to decrement the counter. Will signal all blocked
|
72
|
+
# threads when the `count` reaches zero.
|
73
|
+
#
|
74
|
+
def count_down
|
75
|
+
@mutex.synchronize do
|
76
|
+
@count -= 1 if @count > 0
|
77
|
+
@condition.broadcast if @count == 0
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
# The current value of the counter.
|
82
|
+
#
|
83
|
+
# @return [Fixnum]
|
84
|
+
# The current value of the counter.
|
85
|
+
#
|
86
|
+
def count
|
87
|
+
@mutex.synchronize { @count }
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
class CountDownLatch < MutexCountDownLatch; end
|
92
|
+
end
|
@@ -0,0 +1,196 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
#
|
3
|
+
# Author: Stefano Harding <riddopic@gmail.com>
|
4
|
+
# License: Apache License, Version 2.0
|
5
|
+
# Copyright: (C) 2014-2015 Stefano Harding
|
6
|
+
#
|
7
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
8
|
+
# you may not use this file except in compliance with the License.
|
9
|
+
# You may obtain a copy of the License at
|
10
|
+
#
|
11
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
12
|
+
#
|
13
|
+
# Unless required by applicable law or agreed to in writing, software
|
14
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
15
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
16
|
+
# See the License for the specific language governing permissions and
|
17
|
+
# limitations under the License.
|
18
|
+
#
|
19
|
+
|
20
|
+
require 'thread'
|
21
|
+
require_relative 'obligation'
|
22
|
+
require_relative 'executor_options'
|
23
|
+
require_relative 'immediate_executor'
|
24
|
+
|
25
|
+
module Garcon
|
26
|
+
|
27
|
+
# Lazy evaluation of a block yielding an immutable result. Useful for
|
28
|
+
# expensive operations that may never be needed. It may be non-blocking,
|
29
|
+
# supports the `Obligation` interface, and accepts the injection of custom
|
30
|
+
# executor upon which to execute the block. Processing of block will be
|
31
|
+
# deferred until the first time `#value` is called. At that time the caller
|
32
|
+
# can choose to return immediately and let the block execute asynchronously,
|
33
|
+
# block indefinitely, or block with a timeout.
|
34
|
+
#
|
35
|
+
# When a `Delay` is created its state is set to `pending`. The value and
|
36
|
+
# reason are both `nil`. The first time the `#value` method is called the
|
37
|
+
# enclosed opration will be run and the calling thread will block. Other
|
38
|
+
# threads attempting to call `#value` will block as well. Once the operation
|
39
|
+
# is complete the *value* will be set to the result of the operation or the
|
40
|
+
# *reason* will be set to the raised exception, as appropriate. All threads
|
41
|
+
# blocked on `#value` will return. Subsequent calls to `#value` will
|
42
|
+
# immediately return the cached value. The operation will only be run once.
|
43
|
+
# This means that any side effects created by the operation will only happen
|
44
|
+
# once as well.
|
45
|
+
#
|
46
|
+
# `Delay` includes the `Garcon::Dereferenceable` mixin to support thread
|
47
|
+
# safety of the reference returned by `#value`.
|
48
|
+
#
|
49
|
+
# @!macro [attach] delay_note_regarding_blocking
|
50
|
+
# @note The default behavior of `Delay` is to block indefinitely when
|
51
|
+
# calling either `value` or `wait`, executing the delayed operation on
|
52
|
+
# the current thread. This makes the `timeout` value completely
|
53
|
+
# irrelevant. To enable non-blocking behavior, use the `executor`
|
54
|
+
# constructor option. This will cause the delayed operation to be
|
55
|
+
# execute on the given executor, allowing the call to timeout.
|
56
|
+
#
|
57
|
+
# @see Garcon::Dereferenceable
|
58
|
+
class Delay
|
59
|
+
include Obligation
|
60
|
+
include ExecutorOptions
|
61
|
+
|
62
|
+
# Create a new `Delay` in the `:pending` state.
|
63
|
+
#
|
64
|
+
# @yield the delayed operation to perform
|
65
|
+
#
|
66
|
+
# @raise [ArgumentError] if no block is given
|
67
|
+
#
|
68
|
+
def initialize(opts = {}, &block)
|
69
|
+
raise ArgumentError, 'no block given' unless block_given?
|
70
|
+
init_obligation
|
71
|
+
set_deref_options(opts)
|
72
|
+
@task_executor = get_executor_from(opts)
|
73
|
+
@task = block
|
74
|
+
@state = :pending
|
75
|
+
@computing = false
|
76
|
+
end
|
77
|
+
|
78
|
+
# Return the value this object represents after applying the options
|
79
|
+
# specified by the `#set_deref_options` method. If the delayed operation
|
80
|
+
# raised an exception this method will return nil. The execption object
|
81
|
+
# can be accessed via the `#reason` method.
|
82
|
+
#
|
83
|
+
# @param [Numeric]
|
84
|
+
# Timeout the maximum number of seconds to wait.
|
85
|
+
#
|
86
|
+
# @return [Object] the current value of the object.
|
87
|
+
#
|
88
|
+
def value(timeout = nil)
|
89
|
+
if @task_executor
|
90
|
+
super
|
91
|
+
else
|
92
|
+
mutex.synchronize do
|
93
|
+
execute = @computing = true unless @computing
|
94
|
+
if execute
|
95
|
+
begin
|
96
|
+
set_state(true, @task.call, nil)
|
97
|
+
rescue => e
|
98
|
+
set_state(false, nil, e)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
if @do_nothing_on_deref
|
103
|
+
@value
|
104
|
+
else
|
105
|
+
apply_deref_options(@value)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
# Return the value this object represents after applying the options
|
111
|
+
# specified by the `#set_deref_options` method. If the delayed operation
|
112
|
+
# raised an exception, this method will raise that exception (even when)
|
113
|
+
# the operation has already been executed).
|
114
|
+
#
|
115
|
+
# @param [Numeric]
|
116
|
+
# Timeout the maximum number of seconds to wait.
|
117
|
+
#
|
118
|
+
# @raise [Exception] when `#rejected?` raises `#reason`.
|
119
|
+
#
|
120
|
+
# @return [Object]
|
121
|
+
# The current value of the object.
|
122
|
+
#
|
123
|
+
def value!(timeout = nil)
|
124
|
+
if @task_executor
|
125
|
+
super
|
126
|
+
else
|
127
|
+
result = value
|
128
|
+
raise @reason if @reason
|
129
|
+
result
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
# Return the value this object represents after applying the options
|
134
|
+
# specified by the `#set_deref_options` method.
|
135
|
+
#
|
136
|
+
# @param [Integer]
|
137
|
+
# Timeout (nil) the maximum number of seconds to wait for the value to be
|
138
|
+
# computed. When `nil` the caller will block indefinitely.
|
139
|
+
#
|
140
|
+
# @return [Object] self
|
141
|
+
#
|
142
|
+
def wait(timeout = nil)
|
143
|
+
if @task_executor
|
144
|
+
execute_task_once
|
145
|
+
super(timeout)
|
146
|
+
else
|
147
|
+
value
|
148
|
+
end
|
149
|
+
self
|
150
|
+
end
|
151
|
+
|
152
|
+
# Reconfigures the block returning the value if still `#incomplete?`
|
153
|
+
#
|
154
|
+
# @yield the delayed operation to perform
|
155
|
+
#
|
156
|
+
# @return [true, false] if success
|
157
|
+
#
|
158
|
+
def reconfigure(&block)
|
159
|
+
mutex.lock
|
160
|
+
raise ArgumentError.new('no block given') unless block_given?
|
161
|
+
unless @computing
|
162
|
+
@task = block
|
163
|
+
true
|
164
|
+
else
|
165
|
+
false
|
166
|
+
end
|
167
|
+
ensure
|
168
|
+
mutex.unlock
|
169
|
+
end
|
170
|
+
|
171
|
+
private
|
172
|
+
|
173
|
+
# @!visibility private
|
174
|
+
def execute_task_once
|
175
|
+
mutex.lock
|
176
|
+
execute = @computing = true unless @computing
|
177
|
+
task = @task
|
178
|
+
mutex.unlock
|
179
|
+
|
180
|
+
if execute
|
181
|
+
@task_executor.post do
|
182
|
+
begin
|
183
|
+
result = task.call
|
184
|
+
success = true
|
185
|
+
rescue => e
|
186
|
+
reason = e
|
187
|
+
end
|
188
|
+
mutex.lock
|
189
|
+
set_state(success, result, reason)
|
190
|
+
event.set
|
191
|
+
mutex.unlock
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
@@ -0,0 +1,144 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
#
|
3
|
+
# Author: Stefano Harding <riddopic@gmail.com>
|
4
|
+
# License: Apache License, Version 2.0
|
5
|
+
# Copyright: (C) 2014-2015 Stefano Harding
|
6
|
+
#
|
7
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
8
|
+
# you may not use this file except in compliance with the License.
|
9
|
+
# You may obtain a copy of the License at
|
10
|
+
#
|
11
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
12
|
+
#
|
13
|
+
# Unless required by applicable law or agreed to in writing, software
|
14
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
15
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
16
|
+
# See the License for the specific language governing permissions and
|
17
|
+
# limitations under the License.
|
18
|
+
#
|
19
|
+
|
20
|
+
module Garcon
|
21
|
+
|
22
|
+
# Object references in Ruby are mutable. This can lead to serious problems
|
23
|
+
# when the `#value` of a concurrent object is a mutable reference. Which is
|
24
|
+
# always the case unless the value is a `Fixnum`, `Symbol`, or similar
|
25
|
+
# "primitive" data type. Most classes in this library that expose a `#value`
|
26
|
+
# getter method do so using the `Dereferenceable` mixin module.
|
27
|
+
#
|
28
|
+
# Objects with this mixin can be configured with a few options that can help
|
29
|
+
# protect the program from potentially dangerous operations.
|
30
|
+
#
|
31
|
+
# * `:dup_on_deref`: when true will call the `#dup` method on the `value`
|
32
|
+
# object every time the `#value` method is called (default: false)
|
33
|
+
# * `:freeze_on_deref`: when true will call the `#freeze` method on the
|
34
|
+
# `value` object every time the `#value` method is called (default: false)
|
35
|
+
# * `:copy_on_deref`: when given a `Proc` object the `Proc` will be run every
|
36
|
+
# time the `#value` method is called. The `Proc` will be given the current
|
37
|
+
# `value` as its only parameter and the result returned by the block will
|
38
|
+
# be the return value of the `#value` call. When `nil` this option will be
|
39
|
+
# ignored (default: nil)
|
40
|
+
#
|
41
|
+
module Dereferenceable
|
42
|
+
|
43
|
+
# Return the value this object represents after applying the options
|
44
|
+
# specified by the `#set_deref_options` method.
|
45
|
+
#
|
46
|
+
# When multiple deref options are set the order of operations is strictly
|
47
|
+
# defined. The order of deref operations is:
|
48
|
+
# * `:copy_on_deref`
|
49
|
+
# * `:dup_on_deref`
|
50
|
+
# * `:freeze_on_deref`
|
51
|
+
#
|
52
|
+
# Because of this ordering there is no need to `#freeze` an object created
|
53
|
+
# by a provided `:copy_on_deref` block. Simply set `:freeze_on_deref` to
|
54
|
+
# `true`. Setting both `:dup_on_deref` to `true` and `:freeze_on_deref` to
|
55
|
+
# `true` is as close to the behavior of a "pure" functional language as we
|
56
|
+
# are likely to get in Ruby.
|
57
|
+
#
|
58
|
+
# This method is thread-safe and synchronized with the internal `#mutex`.
|
59
|
+
#
|
60
|
+
# @return [Object]
|
61
|
+
# the current value of the object
|
62
|
+
def value
|
63
|
+
mutex.lock
|
64
|
+
apply_deref_options(@value)
|
65
|
+
ensure
|
66
|
+
mutex.unlock
|
67
|
+
end
|
68
|
+
|
69
|
+
alias_method :deref, :value
|
70
|
+
|
71
|
+
protected # A T T E N Z I O N E A R E A P R O T E T T A
|
72
|
+
|
73
|
+
# Set the internal value of this object
|
74
|
+
#
|
75
|
+
# @param [Object] val
|
76
|
+
# the new value
|
77
|
+
def value=(val)
|
78
|
+
mutex.lock
|
79
|
+
@value = val
|
80
|
+
ensure
|
81
|
+
mutex.unlock
|
82
|
+
end
|
83
|
+
|
84
|
+
# A mutex lock used for synchronizing thread-safe operations. Methods
|
85
|
+
# defined by `Dereferenceable` are synchronized using the `Mutex` returned
|
86
|
+
# from this method. Operations performed by the including class that operate
|
87
|
+
# on the `@value` instance variable should be locked with this `Mutex`.
|
88
|
+
#
|
89
|
+
# @return [Mutex]
|
90
|
+
# the synchronization object
|
91
|
+
def mutex
|
92
|
+
@mutex
|
93
|
+
end
|
94
|
+
|
95
|
+
# Initializes the internal `Mutex`.
|
96
|
+
#
|
97
|
+
# @note
|
98
|
+
# This method *must* be called from within the constructor of the
|
99
|
+
# including class.
|
100
|
+
#
|
101
|
+
# @see #mutex
|
102
|
+
def init_mutex
|
103
|
+
@mutex = Mutex.new
|
104
|
+
end
|
105
|
+
|
106
|
+
# Set the options which define the operations #value performs before
|
107
|
+
# returning data to the caller (dereferencing).
|
108
|
+
#
|
109
|
+
# @note
|
110
|
+
# Most classes that include this module will call `#set_deref_options`
|
111
|
+
# from within the constructor, thus allowing these options to be set at
|
112
|
+
# object creation.
|
113
|
+
#
|
114
|
+
# @param [Hash] opts
|
115
|
+
# the options defining dereference behavior.
|
116
|
+
# @option opts [String] :dup_on_deref (false)
|
117
|
+
# call `#dup` before returning the data
|
118
|
+
# @option opts [String] :freeze_on_deref (false)
|
119
|
+
# call `#freeze` before returning the data
|
120
|
+
# @option opts [String] :copy_on_deref (nil)
|
121
|
+
# call the given `Proc` passing the internal value and returning the value
|
122
|
+
# returned from the proc
|
123
|
+
def set_deref_options(opts = {})
|
124
|
+
mutex.lock
|
125
|
+
@dup_on_deref = opts[:dup_on_deref] || opts[:dup]
|
126
|
+
@freeze_on_deref = opts[:freeze_on_deref] || opts[:freeze]
|
127
|
+
@copy_on_deref = opts[:copy_on_deref] || opts[:copy]
|
128
|
+
@nothing_on_deref = !(@dup_on_deref || @freeze_on_deref || @copy_on_deref)
|
129
|
+
nil
|
130
|
+
ensure
|
131
|
+
mutex.unlock
|
132
|
+
end
|
133
|
+
|
134
|
+
# @!visibility private
|
135
|
+
def apply_deref_options(value)
|
136
|
+
return nil if value.nil?
|
137
|
+
return value if @nothing_on_deref
|
138
|
+
value = @copy_on_deref.call(value) if @copy_on_deref
|
139
|
+
value = value.dup if @dup_on_deref
|
140
|
+
value = value.freeze if @freeze_on_deref
|
141
|
+
value
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|