garcun 0.0.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 +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
|