eventbox 0.1.0 → 1.0.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: 06556e4ddd4d9ccd8cfede0c2163dd164165bdf6585d6a3352a1f44b7d17ff67
4
- data.tar.gz: 6532937e73f20e42808a5516699781ed95284fc1cc52e0d160b443239e690fd6
3
+ metadata.gz: 2cded1e660a5b8321b82e80dfec3bb02f66186d5e9befa33a9083773e67708a4
4
+ data.tar.gz: a49d8755319a94fc72d67ffbbf8aee6c2f3da6c1c35f8530f0ea3e2bd4cfe003
5
5
  SHA512:
6
- metadata.gz: 69dd49a9e166760c2cb7487ef5560ed92a59b68eaf4702dbf08dcab93751ae8bfc2f271f387eae0822b4798f60f8eff7cd644ecf63fd765bf10b42758c37e8ac
7
- data.tar.gz: 66d26b6bf6dcac3954273b216261e576cbd62aec7b8c208eec55b84fd87fe3e1a1b452884cf8a97e2ef772c34020d48d0596ed1483e755a630aace852c7a20e1
6
+ metadata.gz: 1566416c8c226a188e21e5d79e6dcc8ca663854fc87062bc7a4600656bf35c75d81c46226b8ba165962f7730e34161c215cf15d3a1b5529ec74947d4a2b14831
7
+ data.tar.gz: 38f010998ef24d2b650264fcead255f905983948ecdbfd7c56afed59f1d36326ccc10015d6e6783fd8d54a757986be233fbe9fa31d25cd7795ed6a916e149bdc
checksums.yaml.gz.sig CHANGED
Binary file
data.tar.gz.sig CHANGED
Binary file
data/.appveyor.yml CHANGED
@@ -1,8 +1,10 @@
1
+ image: Visual Studio 2019
2
+
1
3
  build: off
2
4
 
3
5
  init:
4
6
  - set PATH=C:/Ruby%ruby_version%/bin;%PATH%
5
- - set RUBYOPT=--verbose -Eutf-8 --enable-frozen-string-literal
7
+ - set RUBYOPT=--verbose --enable-frozen-string-literal
6
8
 
7
9
  install:
8
10
  - ps: |
@@ -24,5 +26,5 @@ environment:
24
26
  RUBYDOWNLOAD: x64
25
27
  - ruby_version: "head"
26
28
  RUBYDOWNLOAD: x86
27
- - ruby_version: "24-x64"
28
- - ruby_version: "24"
29
+ - ruby_version: "30-x64"
30
+ - ruby_version: "30"
data/.travis.yml CHANGED
@@ -3,14 +3,14 @@ sudo: false
3
3
  language: ruby
4
4
  cache: bundler
5
5
  matrix:
6
- fast_finish: true
6
+ fast_finish: false
7
7
  include:
8
- - rvm: 2.3.8
9
- - rvm: 2.5.3
8
+ - rvm: 3.0.1
10
9
  env: RUBYOPT=--verbose --enable-frozen-string-literal
11
- - rvm: jruby-9.2.4.1
12
10
  - rvm: ruby-head
13
11
  env: RUBYOPT=--verbose --enable-frozen-string-literal
14
12
 
13
+ # JRuby and Truffleruby don't support keyword argument semantics of ruby-3.0
14
+
15
15
  script:
16
16
  - bundle exec rake test TESTOPTS=-v
data/.yardopts CHANGED
@@ -3,7 +3,7 @@
3
3
  --protected
4
4
  --private
5
5
  --no-private
6
- --embed-mixin Eventbox::Boxable
6
+ --asset docs/images:images
7
7
  lib/**/*.rb
8
8
  -
9
- docs/**/*.md
9
+ docs/*.md
data/CHANGELOG.md ADDED
@@ -0,0 +1,19 @@
1
+ 1.0.0 / 2021-07-07
2
+ -------------------
3
+
4
+ Changed:
5
+ * Requires ruby-3.0 or newer.
6
+ * More readable #inspect output of several classes.
7
+
8
+ Added:
9
+ * Allow calls to external objects and procs/lambdas from the event scope.
10
+ * Introduce Eventbox#call_context .
11
+ * Correct and consistent handling of keyword arguments to method/prod calls.
12
+ * Allow serialization by marshal_dump and marshal_load.
13
+ * Improved documentation.
14
+
15
+
16
+ 0.1.0 / 2018-12-04
17
+ -------------------
18
+
19
+ * First release
data/README.md CHANGED
@@ -5,21 +5,23 @@
5
5
 
6
6
  _Manage multithreading with the safety of event based programming_
7
7
 
8
+ Eventbox is a model of concurrent computation that is used to build thread-safe objects with arbitrary interfaces.
9
+ It is [kind of advancement](#comparison-threading-abstractions) of the well known [actor model](https://en.wikipedia.org/wiki/Actor_model) leveraging the possibilities of the ruby language.
10
+ It is a small, consistent but powerful threading abstraction which **integrates well into existing environments**.
11
+
8
12
  {Eventbox} objects are event based and single threaded from the inside but thread-safe and blocking from the outside.
9
- Eventbox enforces a separation of code for event processing and code running blocking operations.
13
+ Eventbox enforces a **separation of code for event processing** and code running blocking operations.
10
14
  Code inside an {Eventbox} object is executed non-concurrently and hence shouldn't do any blocking operations.
11
15
  This is similar to the typical JavaScript programming style.
12
16
 
13
- On the other hand all blocking operations can be executed in action threads spawned by the {Eventbox.action action} method type.
14
- Communication between actions and event processing is done through ordinary method or lambda calls.
17
+ On the other hand all **blocking operations can be executed in action threads** spawned by the {Eventbox.action action} method type.
18
+ Communication between actions, event processing and external environment is done through ordinary method and lambda calls.
19
+ They arbitrate between blocking versus event based scheme and ensure thread-safety.
15
20
 
16
21
  An important task of Eventbox is to avoid race conditions through shared data.
17
- Such data races between event scope and external/action scope are avoided through {Eventbox::Sanitizer filters} applied to all inputs and outputs.
22
+ Such data races between event scope and external/action scope are avoided through **{Eventbox::Sanitizer filters} applied to all inputs and outputs**.
18
23
  That way {Eventbox} guarantees stable states while event processing without a need for any locks.
19
24
 
20
- Eventbox is a model of concurrent computation that is used to build thread-safe objects with arbitrary interfaces.
21
- It is kind of [advancement](#comparison-threading-abstractions) of the well known [actor model](https://en.wikipedia.org/wiki/Actor_model) leveraging the possibilities of the ruby language.
22
-
23
25
  * [API documentation](https://www.rubydoc.info/github/larskanis/eventbox/master)
24
26
 
25
27
 
@@ -53,22 +55,23 @@ It can therefore be used to build well known multithread abstractions like a Que
53
55
 
54
56
  ```ruby
55
57
  require "eventbox"
56
- class Queue < Eventbox
57
- # Called at Queue.new just like Object#initialize in ordinary ruby classes
58
+ class MyQueue < Eventbox
59
+ # Called at MyQueue.new just like Object#initialize in ordinary ruby classes
58
60
  async_call def init
59
61
  @que = [] # List of values waiting for being fetched by deq
60
62
  @waiting = [] # List of blocking deq calls waiting for new values to be pushed by enq
61
63
  end
62
64
 
63
- # Push a value to the queue and return the next value by the next waiting deq call
64
- async_call def enq(€value) # €-variables are passed through as reference
65
- @que << €value # Push a value to the queue
66
- if w=@waiting.shift
67
- w.yield @que.shift # Let one waiting deq call return with the next value from the queue
65
+ # Push a value to the queue - async methods always return immediately
66
+ async_call def enq(€value) # €-variables are passed through as reference instead of copies
67
+ @que << €value # Push the value to the queue
68
+ if w=@waiting.shift # Is there a thread already waiting for a value?
69
+ w.yield @que.shift # Let the waiting `deq' call return the oldest value in the queue
68
70
  end
69
71
  end
70
72
 
71
73
  # Fetch a value from the queue or suspend the caller until a value has been enqueued
74
+ # yield methods are completely processed, but return not before a result has been yielded
72
75
  yield_call def deq(result)
73
76
  if @que.empty?
74
77
  @waiting << result # Don't return a value now, but enqueue the request as waiting
@@ -78,13 +81,30 @@ class Queue < Eventbox
78
81
  end
79
82
  end
80
83
  ```
81
- It has semantics like ruby's builtin Queue implementation:
84
+
85
+ <a name="my_queue_image"></a>
86
+ A picture describes it best:
87
+
88
+ [![MyQueue calls](https://raw.github.com/larskanis/eventbox/master/docs/images/my_queue_calls.svg?sanitize=true)](https://www.rubydoc.info/github/larskanis/eventbox/master/file/README.md#my_queue_image)
89
+ {include:file:docs/my_queue_calls_github.md}
90
+
91
+ Although there are no mutex or condition variables in use, the implementation is thread-safe.
92
+ This is due to the wrapping that is activated by {Eventbox::Boxable.async_call async_call} and {Eventbox::Boxable.yield_call yield_call} prefixes.
93
+ The {Eventbox::Boxable.yield_call yield_call} method definition divides the single external call into two internal events: The event of the start of call and the event of releasing the call with a return value.
94
+ In contrast {Eventbox::Boxable.async_call async_call} defines a method which handles one event only - the start of the call: The external call completes immediately and always returns `self`.
95
+
96
+ The branch in `Queue#deq` shows a typical decision taking in Eventbox:
97
+ If the call can be processed immediately it yields the result, else wise the result is added to an internal list to be processes later.
98
+ This list must be checked at each event which could signal the ability to complete the enqueued processing.
99
+ This is done in `Queue#enq` in the above example.
100
+
101
+ Our new queue class unsurprisingly has semantics like ruby's builtin Queue implementation:
82
102
 
83
103
  ```ruby
84
- q = Queue.new
104
+ q = MyQueue.new
85
105
  Thread.new do
86
106
  5.times do |i|
87
- q.enq i # Enqueue integers 0 to 5
107
+ q.enq i # Enqueue integers 0 to 4
88
108
  end
89
109
  end
90
110
 
@@ -100,22 +120,9 @@ end
100
120
  4
101
121
  ```
102
122
 
103
- Although there are no mutex or condition variables in use, the implementation is guaranteed to be thread-safe.
104
- The key feature is the {Eventbox.yield_call} method definition.
105
- It divides the single external call into two internal events: The event of the start of call and the event of releasing the call with a return value.
106
- In contrast {Eventbox.async_call} defines a method which handles one event only - the start of the call.
107
- The external call returns immediately, but can't return a value.
108
-
109
- Seeing curly braces instead of links? Switch to the [API documentation](https://www.rubydoc.info/github/larskanis/eventbox/master).
110
-
111
- The branch in `Queue#deq` shows a typical decision taking in Eventbox:
112
- If the call can be processed immediately it yields the result, else wise the result is added to a list to be processes later.
113
- It's important to check this list at each event which could signal the ability to complete the enqueued processing.
114
- This is done in `Queue#enq` in the above example.
115
-
116
123
  If you just need a queue it's better to stay at the Queue implementations of the standard library or [concurrent-ruby](https://github.com/ruby-concurrency/concurrent-ruby).
117
124
  However if you want to cancel items in the queue for example, you need more control about waiting items or waiting callers than common thread abstractions offer.
118
- The same if you want to query and visualize the internal state of processing - that means the pending items in the queue.
125
+ The same if you want to query and visualize the internal state of processing (the pending items in the queue).
119
126
 
120
127
 
121
128
  ### Hands on
@@ -127,9 +134,12 @@ It is recommended to work them through, in order to fully understand how Eventbo
127
134
  * {file:docs/server.md TCP server} - Understand how to startup and shutdown blocking actions and to combine several Eventbox classes to handle parallel connections.
128
135
  * {file:docs/threadpool.md Thread-pool} - Understand how parallel external requests can be serialized and scheduled.
129
136
 
137
+ Seeing curly braces instead of links? Switch to the [API documentation](https://www.rubydoc.info/github/larskanis/eventbox/master).
138
+
130
139
 
131
140
  ## Method types
132
141
 
142
+ <a name="event-scope"></a>
133
143
  ### Event Scope
134
144
 
135
145
  Eventbox offers 3 different types of external callable methods:
@@ -148,11 +158,12 @@ There is no hard criteria for what is considered a blocking operation, but since
148
158
  If the processing time of an event scope method or block exceeds the limit of 0.5 seconds, a warning is print to `STDERR`.
149
159
  This limit can be changed by {Eventbox.with_options}.
150
160
 
151
- Arguments of async, sync and yield calls can be prefixed by a sign.
161
+ Arguments of async, sync and yield calls can be prefixed by a `€` sign.
152
162
  This marks them as to be passed through as reference, instead of being copied.
153
- A €-variable is wrapped and protected within the event scope, but unwrapped when passed to action or external scope.
163
+ A `€`-variable is wrapped and protected within the event scope, but unwrapped when passed to action or external scope.
164
+ It can be called within the event scope by {Eventbox::ExternalObject#send}.
154
165
 
155
- In additoin there are accessor methods usable as known from ordinary ruby objects: {Eventbox.attr_reader attr_reader}, {Eventbox.attr_writer attr_writer} and {Eventbox.attr_accessor attr_accessor}.
166
+ In addition there are accessor methods usable as known from ordinary ruby objects: {Eventbox.attr_reader attr_reader}, {Eventbox.attr_writer attr_writer} and {Eventbox.attr_accessor attr_accessor}.
156
167
  They allow thread-safe access to instance variables.
157
168
 
158
169
  Beside {Eventbox.async_call async_call}, {Eventbox.sync_call sync_call} and {Eventbox.yield_call yield_call} methods it's possible to define plain `private` methods, since they are not accessible externally.
@@ -178,12 +189,21 @@ And all data generated by the action should be passed as arguments back to event
178
189
  Some data shall just be managed as reference in some scope without being accessed there.
179
190
  Or it is passed through a given scope only.
180
191
  In such cases it can be marked as {Eventbox#shared_object shared_object}.
181
- This wrapping is similar to argument variables, however {Eventbox#shared_object shared_object} it more versatile.
192
+ This wrapping is similar to `€` argument variables, however {Eventbox#shared_object shared_object} it more versatile.
182
193
  It marks objects permanently and wraps them even when they are stored inside of a copied object.
183
194
 
195
+ <a name="external-scope"></a>
184
196
  ### External scope
185
197
 
186
198
  Code outside of the Eventbox class is referred to as "external scope".
199
+ The external scope is recognized as one common space.
200
+
201
+ External objects can be directly called from action scope, since they are unwrapped when passed from event to action scope.
202
+ In this case the object to be called should be thread-safe.
203
+ See also [What is safe and what isn't?](#eventbox-safety) below.
204
+
205
+ External objects can also be called from event scope per {Eventbox::ExternalObject#send}, but with some restrictions.
206
+ The same restrictions apply to external closures too, which are callable from event scope per {Eventbox::ExternalProc#call}.
187
207
 
188
208
 
189
209
  ## Block and Proc types
@@ -197,8 +217,8 @@ Similary to the 3 method calls above there are 3 types of proc objects which act
197
217
 
198
218
  These proc objects can be created within event scope, can be passed to external scope and called from there.
199
219
 
200
- Arguments of async, sync and yield procs can be prefixed by a sign.
201
- In that case, they are passed as reference, equally to €-variables of {Eventbox.async_call async_call}, {Eventbox.sync_call sync_call} and {Eventbox.yield_call yield_call} methods.
220
+ Arguments of async, sync and yield procs can be prefixed by a `€` sign.
221
+ In that case, they are passed as reference, equally to `€`-variables of {Eventbox.async_call async_call}, {Eventbox.sync_call sync_call} and {Eventbox.yield_call yield_call} methods.
202
222
 
203
223
  The other way around - Proc objects or blocks which are defined in external or action scope - can be passed to event scope.
204
224
  Such a Proc object is wrapped as a {Eventbox::ExternalProc} object within the event scope.
@@ -207,6 +227,7 @@ Instead the event scope method is executed until its end, while the block is exe
207
227
  Optionally the block can be called with a completion block as the last argument, which is called with the result of the external proc when it has finished.
208
228
 
209
229
 
230
+ <a name="exceptions"></a>
210
231
  ## Exceptions
211
232
 
212
233
  Eventbox makes use of exceptions in several ways.
@@ -236,10 +257,14 @@ async_call def conn_failed(error)
236
257
  end
237
258
  ```
238
259
 
260
+ Alternatively closures can be passed to the action which are called in case of success or failure.
261
+ See the {file:docs/downloads.md#exceptions-closure-style download example} for more description about this variation.
262
+
239
263
  Another use of exceptions is for sending signals to running actions.
240
264
  This is done by {Eventbox::Action#raise}.
241
265
 
242
266
 
267
+ <a name="eventbox-safety"></a>
243
268
  ## What is safe and what isn't?
244
269
 
245
270
  At each transition of the scope all passing objects are sanitized by the {Eventbox::Sanitizer}.
@@ -247,7 +272,7 @@ It protects the event scope from data races and arbitrates between blocking and
247
272
  This is done by copying or wrapping the objects conveniently as described in the {Eventbox::Sanitizer}.
248
273
  That way event scope methods never get an inconsistent state regardless of the activities of external threads.
249
274
 
250
- Obviously it's not safe to do things like using `send` to call private methods from external, access instance variables per `instance_variable_set` or use global variables in a multithreading context.
275
+ Obviously it's not safe to do things like using `send` to call private methods from external, access instance variables per `instance_variable_set` or use class or global variables in a multithreading context.
251
276
  Such rough ways of communication with an Eventbox object are surely neither recommended nor supported.
252
277
  Other than these the event scope of an Eventbox instance is pretty well protected against accident mistakes.
253
278
 
@@ -263,11 +288,20 @@ External libraries and objects must be thread-safe on its own if used from diffe
263
288
  Protecting them is beyond the scope of Eventbox.
264
289
 
265
290
 
291
+ ## Object distribution and persistence
292
+
293
+ Eventbox objects can be serialized to be stored persistent or distributed across the network.
294
+ This allows the utilization by job schedulers, messaging libraries and distributed object systems.
295
+ However there are the following restrictions:
296
+
297
+ 1. Only objects without running actions can be serialized, since running code can not be serialized.
298
+ 2. Objects with a custom {Eventbox.with_options thread-pool} can be serialized only, when the thread-pool can be serialized.
299
+ 3. Objects with {Eventbox.with_options guard_time} set to a Proc object can not be serialized.
300
+
301
+
266
302
  ## Time based events
267
303
 
268
- It's possible to generate a timer event by stating an action with a `sleep` and subsequent call to an {Eventbox.async_call async_call} method.
269
- However that's not very convenient.
270
- Therefore Eventbox provides a dedicated {Eventbox::Timer timer module} for simple timer functions.
304
+ Although timer events can be easily generated by an action with a `sleep` function, they are so common, that Eventbox bundles a dedicated {Eventbox::Timer timer module} for scheduling timer events.
271
305
  It can be included into Eventbox classes by:
272
306
 
273
307
  ```ruby
@@ -275,6 +309,7 @@ It can be included into Eventbox classes by:
275
309
  ```
276
310
 
277
311
  It offers {Eventbox::Timer#timer_after} and {Eventbox::Timer#timer_every} functions to schedule blocks to be called.
312
+ See the {Eventbox::Timer} module for further description.
278
313
 
279
314
 
280
315
  ## Derived classes and mixins
@@ -313,13 +348,13 @@ This was the primary motivation to develop this library.
313
348
  Eventbox is kind of advancement of the well known [actor model](https://en.wikipedia.org/wiki/Actor_model) leveraging the possibilities of the ruby language.
314
349
  While the actor model uses explicit message passing, Eventbox relies on method calls, closure calls and exceptions, which makes it much more natural to use.
315
350
  Unlike an actor, Eventbox doesn't start a thread per object, but uses the thread of the caller to execute non-blocking code.
316
- This makes instantiation of Eventbox objects cheeper than Actor objects.
351
+ This makes instantiation of Eventbox objects cheaper than Actor objects.
317
352
  Instead it can create and manage in-object private threads in form of {Eventbox.action actions} to be used for blocking operations.
318
353
 
319
354
  Many actor implementations manage an inheritance tree of actor objects.
320
355
  Parent actors are then notified about failures of child actors.
321
356
  In contrast Eventbox objects maintain a list of all running internal actions instead, but are completely independent from each other.
322
- Failures are handled object internal - see chapter "Exceptions" above.
357
+ Failures are handled either object internal or by the caller - see chapter [Exceptions](#exceptions) above.
323
358
 
324
359
  ### Internal state
325
360
 
@@ -341,15 +376,23 @@ Beside this, Eventbox has an explicit specification where blocking and where non
341
376
  This ensures that events are processed in time regardless of the current state.
342
377
  Such a specification is not enforced by most other threading abstractions and can quickly lead to delayed reactions in particular situations.
343
378
 
379
+ ### No global states
380
+
381
+ Eventbox doesn't manage or use any global states other than class definitions.
382
+ Even {Eventbox.with_options configuration options} are handled on a class basis.
383
+ This is why Eventbox can be combined with other threading abstractions and integrated in any applications without side effects.
384
+ Vice versa Eventbox objects can easily replaced by other threading abstractions, if these fit better.
344
385
 
345
- ## Comparison with other async libraries
386
+ ### Comparison with other async libraries
346
387
 
347
- Eventbox doesn't try to implement IO or other blocking operations on top of a global event loop.
388
+ Eventbox doesn't implement any own IO or other kinds of blocking operations.
348
389
  Instead it encourages the use of blocking operations and threads for things which should run in parallel, while keeping the management code in safe internal methods written in an event based style.
349
- Because IO is done in action threads, the only type of events handled by the event scope are method calls received from actions or external calls.
350
- They are processed by a kind of event loop which runs one per Eventbox object.
390
+ Because IO is done in action threads, the only type of events handled by the event scope are method or closure calls received from actions or external.
391
+ They are processed by a kind of local event loop which runs one per Eventbox object.
351
392
 
352
393
  This is in contrast to libraries like [async](https://github.com/socketry/async), [EventMachine](https://github.com/eventmachine/eventmachine) or [Celluloid](https://github.com/celluloid/celluloid) which provide dozens of IO wrappers.
394
+ Due to these differences the focus of Eventbox is on a consistent, solid and accurate core that developers can rely on.
395
+ Intentionally there is no ecosystem around Eventbox.
353
396
 
354
397
 
355
398
  ## Eventbox performance
@@ -362,7 +405,7 @@ And it is written to act as a solid and consistent foundation for a wide range o
362
405
  So if your use case requires raw performance more than implementation safety, Eventbox is probably not the right tool.
363
406
 
364
407
  Still there is lots of room for performance improvements in Eventbox, like faster method invocations or copy-on-write objects.
365
- If there's a stronger interest in Eventbox performance, it's possible to source relevant parts out to a C extension.
408
+ If there's a stronger interest in Eventbox performance, it's even possible to source relevant parts out to a C extension.
366
409
  The introduction of guilds in ruby will probably be helpful for Eventbox as well.
367
410
 
368
411
 
data/docs/downloads.md CHANGED
@@ -1,8 +1,8 @@
1
- ### Use Eventbox to download URLs concurrently
1
+ ## Use Eventbox to download URLs concurrently
2
2
 
3
3
  The following example illustrates how to use actions in order to download a list of URLs in parallel.
4
4
 
5
- At first the `init` method starts an action for each URL to be downloaded, initializes some variables and stores the `result` object for later use.
5
+ At first the {Eventbox#init init} method starts an action for each URL to be downloaded, initializes some variables and stores the `result` object for later use.
6
6
  Since the `result` is not yielded in the method body, the external call to `ParallelDownloads.new` doesn't return to that point in time.
7
7
  Instead it's suspended until `result` is yielded later on, when all URLs have been retrieved.
8
8
 
@@ -53,14 +53,14 @@ class ParallelDownloads < Eventbox.with_options(threadpool: Eventbox::ThreadPool
53
53
  private action def start_download(url)
54
54
  data = OpenURI.open_uri(url) # HTTP GET url
55
55
  .read(100).each_line.first # Retrieve the first line but max 100 bytes
56
- rescue SocketError => err # Catch any network errors
56
+ rescue => err # Catch any network errors
57
57
  download_finished(url, err) # and store it in the result hash
58
58
  else
59
59
  download_finished(url, data) # ... or store the retrieved data when successful
60
60
  end
61
61
 
62
62
  # Called for each finished download
63
- private sync_call def download_finished(url, res)
63
+ private async_call def download_finished(url, res)
64
64
  @downloads[url] = res # Store the download result in the result hash
65
65
  @progress&.yield(@downloads.size) # Notify the caller about our progress
66
66
  if @downloads.size == @urls.size # All downloads finished?
@@ -97,6 +97,7 @@ The order depends on the particular response time of the URL.
97
97
  Since Eventbox protects from data races, it's insignificant in which order events are emitted by an event scope method and whether objects are changed after being sent.
98
98
  It's therefore OK to set `@downloads` both before or after starting the action threads per `start_download` in `init`.
99
99
 
100
+ <a name="exceptions-closure-style"></a>
100
101
  ### Change to closure style
101
102
 
102
103
  There is another alternative way to transmit the result of an action to the event scope.
@@ -113,7 +114,7 @@ class ParallelDownloads < Eventbox.with_options(threadpool: Eventbox::ThreadPool
113
114
  yield_call def init(urls, result, &progress)
114
115
  urls.each do |url| # Start a download thread for each URL
115
116
 
116
- on_finished = sync_proc do |res| # Create a closure object comparable to sync_call
117
+ on_finished = async_proc do |res| # Create a closure object comparable to sync_call
117
118
  @downloads[url] = res # Store the download result in the result hash
118
119
  progress&.yield(@downloads.size) # Notify the caller about our progress
119
120
  if @downloads.size == urls.size # All downloads finished?
@@ -0,0 +1,761 @@
1
+ <!--
2
+ # @markup html
3
+ -->
4
+
5
+ <svg
6
+ xmlns:osb="http://www.openswatchbook.org/uri/2009/osb"
7
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
8
+ xmlns:cc="http://creativecommons.org/ns#"
9
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
10
+ xmlns:svg="http://www.w3.org/2000/svg"
11
+ xmlns="http://www.w3.org/2000/svg"
12
+ xmlns:xlink="http://www.w3.org/1999/xlink"
13
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
14
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
15
+ width="185mm"
16
+ height="142mm"
17
+ viewBox="0 0 129.5 99.400001"
18
+ version="1.1"
19
+ id="svg8"
20
+ inkscape:version="0.92.3 (2405546, 2018-03-11)"
21
+ sodipodi:docname="my_queue_calls.svg">
22
+ <defs
23
+ id="defs2">
24
+ <marker
25
+ inkscape:isstock="true"
26
+ style="overflow:visible;"
27
+ id="marker1378"
28
+ refX="0.0"
29
+ refY="0.0"
30
+ orient="auto"
31
+ inkscape:stockid="Arrow1Lend">
32
+ <path
33
+ transform="scale(0.8) rotate(180) translate(12.5,0)"
34
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1;fill:#000000;fill-opacity:1"
35
+ d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
36
+ id="path1376" />
37
+ </marker>
38
+ <marker
39
+ inkscape:stockid="Arrow1Lend"
40
+ orient="auto"
41
+ refY="0.0"
42
+ refX="0.0"
43
+ id="marker1208"
44
+ style="overflow:visible;"
45
+ inkscape:isstock="true"
46
+ inkscape:collect="always">
47
+ <path
48
+ id="path948"
49
+ d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
50
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1;fill:#000000;fill-opacity:1"
51
+ transform="scale(0.8) rotate(180) translate(12.5,0)" />
52
+ </marker>
53
+ <marker
54
+ inkscape:stockid="Arrow1Lend"
55
+ orient="auto"
56
+ refY="0"
57
+ refX="0"
58
+ id="marker1168"
59
+ style="overflow:visible"
60
+ inkscape:isstock="true">
61
+ <path
62
+ id="path908"
63
+ d="M 0,0 5,-5 -12.5,0 5,5 Z"
64
+ style="fill:#ffffeb;fill-opacity:1;fill-rule:evenodd;stroke:#ee0000;stroke-width:1.00000003pt;stroke-opacity:1"
65
+ transform="matrix(-0.8,0,0,-0.8,-10,0)"
66
+ inkscape:connector-curvature="0" />
67
+ </marker>
68
+ <marker
69
+ inkscape:isstock="true"
70
+ style="overflow:visible"
71
+ id="marker928"
72
+ refX="0"
73
+ refY="0"
74
+ orient="auto"
75
+ inkscape:stockid="Arrow1Lend">
76
+ <path
77
+ transform="matrix(-0.8,0,0,-0.8,-10,0)"
78
+ style="fill:#0000ff;fill-opacity:1;fill-rule:evenodd;stroke:#0000ff;stroke-width:1.00000003pt;stroke-opacity:1"
79
+ d="M 0,0 5,-5 -12.5,0 5,5 Z"
80
+ id="path926"
81
+ inkscape:connector-curvature="0" />
82
+ </marker>
83
+ <marker
84
+ inkscape:stockid="Arrow1Lend"
85
+ orient="auto"
86
+ refY="0"
87
+ refX="0"
88
+ id="marker10624"
89
+ style="overflow:visible"
90
+ inkscape:isstock="true"
91
+ inkscape:collect="always">
92
+ <path
93
+ id="path10622"
94
+ d="M 0,0 5,-5 -12.5,0 5,5 Z"
95
+ style="fill:#0000ff;fill-opacity:1;fill-rule:evenodd;stroke:#0000ff;stroke-width:1.00000003pt;stroke-opacity:1"
96
+ transform="matrix(-0.8,0,0,-0.8,-10,0)"
97
+ inkscape:connector-curvature="0" />
98
+ </marker>
99
+ <marker
100
+ inkscape:stockid="Arrow1Lend"
101
+ orient="auto"
102
+ refY="0"
103
+ refX="0"
104
+ id="marker10556"
105
+ style="overflow:visible"
106
+ inkscape:isstock="true">
107
+ <path
108
+ id="path10554"
109
+ d="M 0,0 5,-5 -12.5,0 5,5 Z"
110
+ style="fill:#ff0000;fill-opacity:1;fill-rule:evenodd;stroke:#ff0000;stroke-width:1.00000003pt;stroke-opacity:1"
111
+ transform="matrix(-0.8,0,0,-0.8,-10,0)"
112
+ inkscape:connector-curvature="0" />
113
+ </marker>
114
+ <marker
115
+ inkscape:stockid="Arrow1Lend"
116
+ orient="auto"
117
+ refY="0"
118
+ refX="0"
119
+ id="marker10502"
120
+ style="overflow:visible"
121
+ inkscape:isstock="true">
122
+ <path
123
+ id="path10500"
124
+ d="M 0,0 5,-5 -12.5,0 5,5 Z"
125
+ style="fill:#ff0000;fill-opacity:1;fill-rule:evenodd;stroke:#ff0000;stroke-width:1.00000003pt;stroke-opacity:1"
126
+ transform="matrix(-0.8,0,0,-0.8,-10,0)"
127
+ inkscape:connector-curvature="0" />
128
+ </marker>
129
+ <marker
130
+ inkscape:stockid="Arrow1Lend"
131
+ orient="auto"
132
+ refY="0"
133
+ refX="0"
134
+ id="marker9930"
135
+ style="overflow:visible"
136
+ inkscape:isstock="true">
137
+ <path
138
+ id="path9928"
139
+ d="M 0,0 5,-5 -12.5,0 5,5 Z"
140
+ style="fill:#f90000;fill-opacity:1;fill-rule:evenodd;stroke:#f90000;stroke-width:1.00000003pt;stroke-opacity:1"
141
+ transform="matrix(-0.8,0,0,-0.8,-10,0)"
142
+ inkscape:connector-curvature="0" />
143
+ </marker>
144
+ <marker
145
+ inkscape:isstock="true"
146
+ style="overflow:visible"
147
+ id="marker4910"
148
+ refX="0"
149
+ refY="0"
150
+ orient="auto"
151
+ inkscape:stockid="Arrow1Lend">
152
+ <path
153
+ transform="matrix(-0.8,0,0,-0.8,-10,0)"
154
+ style="fill:#f50000;fill-opacity:1;fill-rule:evenodd;stroke:#f50000;stroke-width:1.00000003pt;stroke-opacity:1"
155
+ d="M 0,0 5,-5 -12.5,0 5,5 Z"
156
+ id="path4908"
157
+ inkscape:connector-curvature="0" />
158
+ </marker>
159
+ <marker
160
+ inkscape:stockid="Arrow1Lend"
161
+ orient="auto"
162
+ refY="0"
163
+ refX="0"
164
+ id="Arrow1Lend"
165
+ style="overflow:visible"
166
+ inkscape:isstock="true"
167
+ inkscape:collect="always">
168
+ <path
169
+ id="path4537"
170
+ d="M 0,0 5,-5 -12.5,0 5,5 Z"
171
+ style="fill:#fa0000;fill-opacity:1;fill-rule:evenodd;stroke:#fa0000;stroke-width:1.00000003pt;stroke-opacity:1"
172
+ transform="matrix(-0.8,0,0,-0.8,-10,0)"
173
+ inkscape:connector-curvature="0" />
174
+ </marker>
175
+ <linearGradient
176
+ id="linearGradient4522"
177
+ osb:paint="solid">
178
+ <stop
179
+ style="stop-color:#000000;stop-opacity:1;"
180
+ offset="0"
181
+ id="stop4520" />
182
+ </linearGradient>
183
+ </defs>
184
+ <sodipodi:namedview
185
+ id="base"
186
+ pagecolor="#ffffff"
187
+ bordercolor="#666666"
188
+ borderopacity="1.0"
189
+ inkscape:pageopacity="0.0"
190
+ inkscape:pageshadow="2"
191
+ inkscape:zoom="1"
192
+ inkscape:cx="373.74533"
193
+ inkscape:cy="265.12062"
194
+ inkscape:document-units="mm"
195
+ inkscape:current-layer="layer1"
196
+ showgrid="false"
197
+ inkscape:snap-nodes="false"
198
+ inkscape:window-width="1252"
199
+ inkscape:window-height="720"
200
+ inkscape:window-x="28"
201
+ inkscape:window-y="0"
202
+ inkscape:window-maximized="1"
203
+ scale-x="0.7" />
204
+ <metadata
205
+ id="metadata5">
206
+ <rdf:RDF>
207
+ <cc:Work
208
+ rdf:about="">
209
+ <dc:format>image/svg+xml</dc:format>
210
+ <dc:type
211
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
212
+ <dc:title />
213
+ </cc:Work>
214
+ </rdf:RDF>
215
+ </metadata>
216
+ <g
217
+ inkscape:label="Ebene 1"
218
+ inkscape:groupmode="layer"
219
+ id="layer1"
220
+ transform="translate(0,-167.6)">
221
+ <rect
222
+ id="rect10"
223
+ width="91.673447"
224
+ height="86.87912"
225
+ x="19.489588"
226
+ y="179.25435"
227
+ rx="0"
228
+ ry="0"
229
+ style="fill:#ffff25;fill-opacity:1;stroke:#000000;stroke-width:0.26458332;stroke-opacity:1" />
230
+ <rect
231
+ y="186.75443"
232
+ x="42.474762"
233
+ height="78.274651"
234
+ width="42.228584"
235
+ id="rect4528"
236
+ style="fill:#ffcfff;fill-opacity:1;stroke:#000000;stroke-width:0.26458335;stroke-opacity:1" />
237
+ <path
238
+ style="fill:none;fill-opacity:1;stroke:#fa0000;stroke-width:0.26499999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#Arrow1Lend)"
239
+ d="M 6.9279211,181.36207 51.561932,205.15991"
240
+ id="path4532"
241
+ inkscape:connector-curvature="0" />
242
+ <rect
243
+ style="fill:none;fill-opacity:1;stroke:#fa0000;stroke-width:0.26500002;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
244
+ id="rect4816"
245
+ width="12.828883"
246
+ height="12.562"
247
+ x="47.018349"
248
+ y="206.254" />
249
+ <path
250
+ style="fill:none;stroke:#f50000;stroke-width:0.26499999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#marker4910)"
251
+ d="M 51.830151,219.0522 8.5333361,235.09765"
252
+ id="path4824"
253
+ inkscape:connector-curvature="0"
254
+ sodipodi:nodetypes="cc" />
255
+ <path
256
+ sodipodi:type="spiral"
257
+ style="fill:#ffffeb;fill-opacity:1;fill-rule:evenodd;stroke:#ee0000;stroke-width:0.42626965;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#marker1168);image-rendering:auto"
258
+ id="path4942"
259
+ sodipodi:cx="18.441597"
260
+ sodipodi:cy="66.346397"
261
+ sodipodi:expansion="1"
262
+ sodipodi:revolution="3.0059199"
263
+ sodipodi:radius="17.37999"
264
+ sodipodi:argument="-17.866762"
265
+ sodipodi:t0="0.6560148"
266
+ d="M 26.332514,74.57612 C 21.896357,79.574502 14.036872,79.637691 9.1685147,75.23772 3.6278489,70.230124 3.5838485,61.445599 8.5498677,56.029955 14.128266,49.946488 23.838892,49.921766 29.801399,55.454262 c 6.626649,6.14874 6.632031,16.786244 0.53274,23.295297 -0.842512,0.899114 -1.781959,1.706899 -2.796296,2.406374"
267
+ transform="matrix(0.62478665,0,0,0.61857331,84.213942,200.00264)" />
268
+ <path
269
+ style="fill:none;stroke:#f90000;stroke-width:0.26499999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#marker9930)"
270
+ d="m 96.207354,249.79477 22.454726,16.57189"
271
+ id="path9918"
272
+ inkscape:connector-curvature="0"
273
+ sodipodi:nodetypes="cc" />
274
+ <rect
275
+ style="fill:none;fill-opacity:1;stroke:#ee0000;stroke-width:0.26499999;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
276
+ id="rect10496"
277
+ width="13.096148"
278
+ height="12.561667"
279
+ x="66.796196"
280
+ y="206.25435" />
281
+ <path
282
+ style="fill:none;stroke:#ff0000;stroke-width:0.26499999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#marker10502)"
283
+ d="M 117.84573,185.10297 74.281541,204.8956"
284
+ id="path10498"
285
+ inkscape:connector-curvature="0" />
286
+ <path
287
+ style="fill:none;stroke:#ff0000;stroke-width:0.26499999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#marker10556)"
288
+ d="m 76.158622,219.01967 18.712932,12.59301"
289
+ id="path10552"
290
+ inkscape:connector-curvature="0" />
291
+ <a
292
+ id="a1025"
293
+ target=""
294
+ transform="translate(-0.555625,-23.151042)"
295
+ xlink:href="https://www.rubydoc.info/github/larskanis/eventbox/master/Eventbox/Sanitizer">
296
+ <ellipse
297
+ onclick=""
298
+ transform="matrix(0.89625618,-0.44353677,0.47919017,0.8777111,0,0)"
299
+ cx="-17.232189"
300
+ cy="238.3199"
301
+ rx="5.3748827"
302
+ ry="10.333156"
303
+ id="path10612"
304
+ style="fill:#ffffff;fill-opacity:1;stroke:#007800;stroke-width:0.34833023;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
305
+ </a>
306
+ <path
307
+ style="fill:none;stroke:#0000ff;stroke-width:0.26499999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#marker10624)"
308
+ d="m 53.165548,215.24192 c 0,0 -9.262697,45.65359 32.874156,23.98422"
309
+ id="path10620"
310
+ inkscape:connector-curvature="0"
311
+ sodipodi:nodetypes="cc" />
312
+ <a
313
+ xlink:href="../Eventbox/Sanitizer"
314
+ transform="translate(0,6.2970833)"
315
+ id="a4631">
316
+ <path
317
+ onclick="https://www.rubydoc.info/github/larskanis/eventbox/master/Eventbox/Sanitizer"
318
+ sodipodi:open="true"
319
+ transform="matrix(0.92881568,0.37054207,-0.47054194,0.88237763,0,0)"
320
+ d="m 114.76537,172.63952 a 5.2028461,8.8422928 0 0 1 -3.99824,-10.43402 5.2028461,8.8422928 0 0 1 6.10517,-6.88361 5.2028461,8.8422928 0 0 1 4.10216,10.31685 5.2028461,8.8422928 0 0 1 -6.03535,7.05921"
321
+ sodipodi:end="1.7491596"
322
+ sodipodi:start="1.7832031"
323
+ sodipodi:ry="8.8422928"
324
+ sodipodi:rx="5.2028461"
325
+ sodipodi:cy="163.99594"
326
+ sodipodi:cx="115.8622"
327
+ sodipodi:type="arc"
328
+ id="path10686"
329
+ style="fill:#ffffff;fill-opacity:1;stroke:#007800;stroke-width:0.32815173;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
330
+ </a>
331
+ <a
332
+ xlink:href="../Eventbox/Sanitizer"
333
+ transform="translate(0,6.2970833)"
334
+ id="a925"
335
+ target="">
336
+ <path
337
+ onclick="https://www.rubydoc.info/github/larskanis/eventbox/master/Eventbox/Sanitizer"
338
+ sodipodi:open="true"
339
+ transform="matrix(0.8488041,0.52870748,-0.64396364,0.7650561,0,0)"
340
+ d="m 247.74739,159.80509 a 4.7844305,9.6128368 0 0 1 -4.70978,9.68959 4.7844305,9.6128368 0 0 1 -4.85765,-9.39028 4.7844305,9.6128368 0 0 1 4.63728,-9.82976 4.7844305,9.6128368 0 0 1 4.92686,9.24357"
341
+ sodipodi:end="6.2452272"
342
+ sodipodi:start="6.2750789"
343
+ sodipodi:ry="9.6128368"
344
+ sodipodi:rx="4.7844305"
345
+ sodipodi:cy="159.88301"
346
+ sodipodi:cx="242.96312"
347
+ sodipodi:type="arc"
348
+ id="path10694"
349
+ style="fill:#ffffff;fill-opacity:1;stroke:#007800;stroke-width:0.32306719;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
350
+ </a>
351
+ <text
352
+ xml:space="preserve"
353
+ style="font-style:normal;font-weight:normal;font-size:2.96333332px;line-height:1.25;font-family:sans-serif;text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332;"
354
+ x="-152.70258"
355
+ y="122.78255"
356
+ id="text10838"
357
+ transform="rotate(-59.850075)"><tspan
358
+ sodipodi:role="line"
359
+ x="-152.70258"
360
+ y="122.78255"
361
+ style="font-size:2.96333332px;text-align:center;text-anchor:middle;stroke-width:0.26458332;"
362
+ id="tspan10848">sanitize</tspan><tspan
363
+ sodipodi:role="line"
364
+ x="-152.70258"
365
+ y="127.19227"
366
+ style="font-size:2.96333332px;text-align:center;text-anchor:middle;stroke-width:0.26458332;"
367
+ id="tspan10852">args</tspan></text>
368
+ <text
369
+ transform="rotate(59.31363)"
370
+ id="text10858"
371
+ y="13.567711"
372
+ x="216.40395"
373
+ style="font-style:normal;font-weight:normal;font-size:2.96333332px;line-height:1.25;font-family:sans-serif;text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332;"
374
+ xml:space="preserve"><tspan
375
+ id="tspan10854"
376
+ style="font-size:2.96333332px;text-align:center;text-anchor:middle;stroke-width:0.26458332;"
377
+ y="13.567711"
378
+ x="216.40395"
379
+ sodipodi:role="line">sanitize</tspan><tspan
380
+ id="tspan10856"
381
+ style="font-size:2.96333332px;text-align:center;text-anchor:middle;stroke-width:0.26458332;"
382
+ y="17.977434"
383
+ x="216.40395"
384
+ sodipodi:role="line">args</tspan></text>
385
+ <text
386
+ transform="rotate(-46.618563)"
387
+ id="text10881"
388
+ y="250.48445"
389
+ x="-115.84221"
390
+ style="font-style:normal;font-weight:normal;font-size:2.96333332px;line-height:1.25;font-family:sans-serif;text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332;"
391
+ xml:space="preserve"><tspan
392
+ style="font-size:2.96333332px;text-align:center;text-anchor:middle;stroke-width:0.26458332;"
393
+ y="250.48445"
394
+ x="-115.84221"
395
+ sodipodi:role="line"
396
+ id="tspan867">sanitize</tspan><tspan
397
+ style="font-size:2.96333332px;text-align:center;text-anchor:middle;stroke-width:0.26458332;"
398
+ y="254.89418"
399
+ x="-115.84221"
400
+ sodipodi:role="line"
401
+ id="tspan923">result</tspan></text>
402
+ <text
403
+ xml:space="preserve"
404
+ style="font-style:normal;font-weight:normal;font-size:2.96333332px;line-height:1.25;font-family:sans-serif;text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332;"
405
+ x="96.894981"
406
+ y="237.41592"
407
+ id="text873"><tspan
408
+ sodipodi:role="line"
409
+ x="96.894981"
410
+ y="237.41592"
411
+ style="font-size:2.96333332px;text-align:center;text-anchor:middle;stroke-width:0.26458332;"
412
+ id="tspan903">wait for</tspan><tspan
413
+ sodipodi:role="line"
414
+ x="96.894981"
415
+ y="241.82565"
416
+ style="font-size:2.96333332px;text-align:center;text-anchor:middle;stroke-width:0.26458332;"
417
+ id="tspan884">result or</tspan><tspan
418
+ sodipodi:role="line"
419
+ x="96.894981"
420
+ y="246.23537"
421
+ style="font-size:2.96333332px;text-align:center;text-anchor:middle;stroke-width:0.26458332;"
422
+ id="tspan902">callback</tspan></text>
423
+ <path
424
+ inkscape:connector-curvature="0"
425
+ id="path924"
426
+ d="m 70.160202,215.24192 c 0,0 1.163455,23.98422 15.900542,23.98422"
427
+ style="fill:none;stroke:#0000ff;stroke-width:0.26499999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#marker928)" />
428
+ <path
429
+ style="fill:#ffffff;fill-opacity:1;stroke:#007800;stroke-width:0.2027204;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
430
+ id="path877"
431
+ sodipodi:type="arc"
432
+ sodipodi:cx="-81.083595"
433
+ sodipodi:cy="244.79619"
434
+ sodipodi:rx="10.626561"
435
+ sodipodi:ry="4.9516382"
436
+ sodipodi:start="3.8273013"
437
+ sodipodi:end="3.8141906"
438
+ d="m -89.30824,241.6607 a 10.626561,4.9516382 0 0 1 14.926619,-0.70719 10.626561,4.9516382 0 0 1 1.566604,6.95297 10.626561,4.9516382 0 0 1 -14.91635,0.75278 10.626561,4.9516382 0 0 1 -1.664385,-6.94804"
439
+ transform="matrix(0.8900354,-0.45589142,0.59689097,0.80232236,0,0)"
440
+ sodipodi:open="true" />
441
+ <text
442
+ xml:space="preserve"
443
+ style="font-style:normal;font-weight:normal;font-size:2.96333332px;line-height:1.25;font-family:sans-serif;text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332;"
444
+ x="-40.597473"
445
+ y="240.45992"
446
+ id="text881"
447
+ transform="rotate(-27.245601)"><tspan
448
+ sodipodi:role="line"
449
+ x="-40.597473"
450
+ y="240.45992"
451
+ style="font-size:2.96333332px;text-align:center;text-anchor:middle;stroke-width:0.26458332;"
452
+ id="tspan899">if someth.</tspan><tspan
453
+ sodipodi:role="line"
454
+ x="-40.597473"
455
+ y="244.86964"
456
+ style="font-size:2.96333332px;text-align:center;text-anchor:middle;stroke-width:0.26458332;"
457
+ id="tspan911">in queue</tspan></text>
458
+ <path
459
+ transform="matrix(0.88469021,-0.46617941,0.63817391,0.76989224,0,0)"
460
+ d="m -100.19975,237.55663 a 11.223675,5.1413383 0 0 1 -11.16623,5.14127 11.223675,5.1413383 0 0 1 -11.28053,-5.08865 11.223675,5.1413383 0 0 1 11.05077,-5.19336 11.223675,5.1413383 0 0 1 11.39364,5.03549"
461
+ sodipodi:end="6.2627144"
462
+ sodipodi:start="0"
463
+ sodipodi:ry="5.1413383"
464
+ sodipodi:rx="11.223675"
465
+ sodipodi:cy="237.55663"
466
+ sodipodi:cx="-111.42342"
467
+ sodipodi:type="arc"
468
+ id="path885"
469
+ style="fill:#ffffff;fill-opacity:1;stroke:#007800;stroke-width:0.21229133;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
470
+ sodipodi:open="true" />
471
+ <text
472
+ transform="rotate(-22.489804)"
473
+ id="text891"
474
+ y="236.29482"
475
+ x="-40.395329"
476
+ style="font-style:normal;font-weight:normal;font-size:2.96333332px;line-height:1.25;font-family:sans-serif;text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332;"
477
+ xml:space="preserve"><tspan
478
+ style="font-size:2.96333332px;text-align:center;text-anchor:middle;stroke-width:0.26458332;"
479
+ y="236.29482"
480
+ x="-40.395329"
481
+ sodipodi:role="line"
482
+ id="tspan915">if a deq</tspan><tspan
483
+ style="font-size:2.96333332px;text-align:center;text-anchor:middle;stroke-width:0.26458332;"
484
+ y="240.70454"
485
+ x="-40.395329"
486
+ sodipodi:role="line"
487
+ id="tspan919">call waits</tspan></text>
488
+ <text
489
+ xml:space="preserve"
490
+ style="font-style:normal;font-weight:normal;font-size:10.58333302px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332"
491
+ x="48.087425"
492
+ y="210.23882"
493
+ id="text909"><tspan
494
+ sodipodi:role="line"
495
+ id="tspan907"
496
+ x="48.087425"
497
+ y="219.60258"
498
+ style="stroke-width:0.26458332" /></text>
499
+ <text
500
+ xml:space="preserve"
501
+ style="font-style:normal;font-weight:normal;font-size:4.58611107px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332"
502
+ x="48.354698"
503
+ y="211.57516"
504
+ id="text875"><tspan
505
+ sodipodi:role="line"
506
+ id="tspan873"
507
+ x="48.354698"
508
+ y="211.57516"
509
+ style="font-size:4.58611107px;stroke-width:0.26458332">enq</tspan></text>
510
+ <text
511
+ xml:space="preserve"
512
+ style="font-style:normal;font-weight:normal;font-size:4.58611107px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332"
513
+ x="68.132637"
514
+ y="211.62103"
515
+ id="text879"><tspan
516
+ sodipodi:role="line"
517
+ id="tspan877"
518
+ x="68.132637"
519
+ y="211.62103"
520
+ style="font-size:4.58611107px;stroke-width:0.26458332">deq</tspan></text>
521
+ <a
522
+ xlink:href="../file/README.md#event-scope"
523
+ transform="translate(0,6.2970833)"
524
+ id="a4641">
525
+ <text
526
+ xml:space="preserve"
527
+ style="font-style:normal;font-weight:normal;font-size:3.52777767px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332"
528
+ x="53.247608"
529
+ y="184.36324"
530
+ id="text888"
531
+ onclick="../file/README.md#Event_Scope"><tspan
532
+ sodipodi:role="line"
533
+ id="tspan886"
534
+ x="53.247608"
535
+ y="184.36324"
536
+ style="font-size:3.52777767px;stroke-width:0.26458332">Event Scope</tspan></text>
537
+ </a>
538
+ <a
539
+ xlink:href="../Eventbox/Boxable#async_call-instance_method"
540
+ transform="translate(0,6.2970833)"
541
+ id="a4644">
542
+ <text
543
+ id="text892"
544
+ y="184.96559"
545
+ x="0.51345128"
546
+ style="font-style:normal;font-weight:normal;font-size:2.96333332px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332;"
547
+ xml:space="preserve"><tspan
548
+ id="tspan894"
549
+ style="font-size:2.96333332px;stroke-width:0.26458332;"
550
+ y="184.96559"
551
+ x="0.51345128"
552
+ sodipodi:role="line">external</tspan><tspan
553
+ id="tspan898"
554
+ style="font-size:2.96333332px;stroke-width:0.26458332;"
555
+ y="189.37532"
556
+ x="0.51345128"
557
+ sodipodi:role="line">call to</tspan><tspan
558
+ id="tspan905"
559
+ style="font-size:2.96333332px;stroke-width:0.26458332;"
560
+ y="193.78503"
561
+ x="0.51345128"
562
+ sodipodi:role="line">async</tspan><tspan
563
+ id="tspan909"
564
+ style="font-size:2.96333332px;stroke-width:0.26458332;"
565
+ y="198.19476"
566
+ x="0.51345128"
567
+ sodipodi:role="line">method</tspan></text>
568
+ </a>
569
+ <text
570
+ xml:space="preserve"
571
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.96333332px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332;"
572
+ x="0.71974826"
573
+ y="225.93076"
574
+ id="text902"
575
+ onclick="Eventbox/Boxable#yield_call-instance_method"><tspan
576
+ sodipodi:role="line"
577
+ x="0.71974826"
578
+ y="225.93076"
579
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.96333332px;font-family:sans-serif;-inkscape-font-specification:sans-serif;stroke-width:0.26458332;"
580
+ id="tspan904">... returns</tspan><tspan
581
+ sodipodi:role="line"
582
+ x="0.71974826"
583
+ y="230.34048"
584
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.96333332px;font-family:sans-serif;-inkscape-font-specification:sans-serif;stroke-width:0.26458332;"
585
+ id="tspan906">immediat.</tspan></text>
586
+ <a
587
+ xlink:href="../Eventbox/Boxable#yield_call-instance_method"
588
+ transform="translate(0,6.2970833)"
589
+ id="a4634">
590
+ <text
591
+ xml:space="preserve"
592
+ style="font-style:normal;font-weight:normal;font-size:2.96333332px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332;"
593
+ x="112.76665"
594
+ y="185.76741"
595
+ id="text912"><tspan
596
+ sodipodi:role="line"
597
+ x="112.76665"
598
+ y="185.76741"
599
+ style="font-size:2.96333332px;stroke-width:0.26458332;"
600
+ id="tspan908">external</tspan><tspan
601
+ sodipodi:role="line"
602
+ x="112.76665"
603
+ y="190.17714"
604
+ style="font-size:2.96333332px;stroke-width:0.26458332;"
605
+ id="tspan910">call to</tspan><tspan
606
+ id="tspan927"
607
+ sodipodi:role="line"
608
+ x="112.76665"
609
+ y="194.58685"
610
+ style="font-size:2.96333332px;stroke-width:0.26458332;">yield</tspan><tspan
611
+ id="tspan929"
612
+ sodipodi:role="line"
613
+ x="112.76665"
614
+ y="198.99658"
615
+ style="font-size:2.96333332px;stroke-width:0.26458332;">method</tspan></text>
616
+ </a>
617
+ <text
618
+ id="text920"
619
+ y="243.64752"
620
+ x="113.03392"
621
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.96333332px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332;"
622
+ xml:space="preserve"
623
+ onclick=""><tspan
624
+ id="tspan916"
625
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.96333332px;font-family:sans-serif;-inkscape-font-specification:sans-serif;stroke-width:0.26458332;"
626
+ y="243.64752"
627
+ x="113.03392"
628
+ sodipodi:role="line">... returns</tspan><tspan
629
+ id="tspan918"
630
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.96333332px;font-family:sans-serif;-inkscape-font-specification:sans-serif;stroke-width:0.26458332;"
631
+ y="248.05725"
632
+ x="113.03392"
633
+ sodipodi:role="line">when</tspan><tspan
634
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.96333332px;font-family:sans-serif;-inkscape-font-specification:sans-serif;stroke-width:0.26458332;"
635
+ y="252.46696"
636
+ x="113.03392"
637
+ sodipodi:role="line"
638
+ id="tspan925">value</tspan><tspan
639
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.96333332px;font-family:sans-serif;-inkscape-font-specification:sans-serif;stroke-width:0.26458332;"
640
+ y="256.87668"
641
+ x="113.03392"
642
+ sodipodi:role="line"
643
+ id="tspan922">yielded</tspan></text>
644
+ <path
645
+ style="fill:#ffffff;fill-opacity:1;stroke:#007800;stroke-width:0.26499999;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
646
+ id="path936"
647
+ sodipodi:type="arc"
648
+ sodipodi:cx="202.16064"
649
+ sodipodi:cy="108.06693"
650
+ sodipodi:rx="9.7553377"
651
+ sodipodi:ry="5.6126599"
652
+ sodipodi:start="0"
653
+ sodipodi:end="6.2820258"
654
+ d="m 211.91598,108.06693 a 9.7553377,5.6126599 0 0 1 -9.75251,5.61266 9.7553377,5.6126599 0 0 1 -9.75816,-5.6094 9.7553377,5.6126599 0 0 1 9.74685,-5.61592 9.7553377,5.6126599 0 0 1 9.76382,5.60615"
655
+ transform="rotate(54.360684)"
656
+ sodipodi:open="true" />
657
+ <a
658
+ xlink:href="../file/README.md#external-scope"
659
+ transform="translate(-17.78)"
660
+ id="a4651">
661
+ <text
662
+ onclick="window.location.href='../file/README.md#External_Scope'"
663
+ id="text903"
664
+ y="170.53304"
665
+ x="51.607788"
666
+ style="font-style:normal;font-weight:normal;font-size:3.52777767px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332"
667
+ xml:space="preserve"><tspan
668
+ style="font-size:3.52777767px;stroke-width:0.26458332"
669
+ y="170.53304"
670
+ x="51.607788"
671
+ id="tspan901"
672
+ sodipodi:role="line">External Scope (outside of the class)</tspan></text>
673
+ </a>
674
+ <text
675
+ xml:space="preserve"
676
+ style="font-style:normal;font-weight:normal;font-size:2.96333332px;line-height:1.25;font-family:sans-serif;text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332;"
677
+ x="202.64171"
678
+ y="107.0413"
679
+ id="text932"
680
+ transform="rotate(54.360684)"><tspan
681
+ sodipodi:role="line"
682
+ id="tspan930"
683
+ x="202.64171"
684
+ y="107.0413"
685
+ style="font-size:2.96333332px;text-align:center;text-anchor:middle;stroke-width:0.26458332;">return</tspan><tspan
686
+ sodipodi:role="line"
687
+ x="202.64171"
688
+ y="111.45102"
689
+ style="font-size:2.96333332px;text-align:center;text-anchor:middle;stroke-width:0.26458332;"
690
+ id="tspan934">self</tspan></text>
691
+ <text
692
+ xml:space="preserve"
693
+ style="font-style:normal;font-weight:normal;font-size:3.52636671px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.18520834"
694
+ x="23.706667"
695
+ y="182.73022"
696
+ id="text933"><tspan
697
+ sodipodi:role="line"
698
+ id="tspan931"
699
+ x="23.706667"
700
+ y="182.73022"
701
+ style="font-size:3.52636671px;stroke-width:0.18520834">Wrapping Layer around methods and closures</tspan></text>
702
+ <text
703
+ xml:space="preserve"
704
+ style="font-style:normal;font-weight:normal;font-size:2.96333332px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.18520834;"
705
+ x="56.858959"
706
+ y="196.80605"
707
+ id="text937"><tspan
708
+ sodipodi:role="line"
709
+ id="tspan935"
710
+ x="56.858959"
711
+ y="196.80605"
712
+ style="font-size:2.96333332px;stroke-width:0.18520834;">@que</tspan><tspan
713
+ sodipodi:role="line"
714
+ x="56.858959"
715
+ y="200.81889"
716
+ style="font-size:2.96333332px;stroke-width:0.18520834;"
717
+ id="tspan939">@waiting</tspan></text>
718
+ <path
719
+ style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:0.1855;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#marker1378)"
720
+ d="m 55.377291,207.35367 3.889375,-5.74554"
721
+ id="path941"
722
+ inkscape:connector-curvature="0" />
723
+ <path
724
+ style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:0.1855;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#marker1208)"
725
+ d="m 69.638332,207.36291 -2.2225,-5.92666"
726
+ id="path943"
727
+ inkscape:connector-curvature="0" />
728
+ <text
729
+ xml:space="preserve"
730
+ style="font-style:normal;font-weight:normal;font-size:2.96333332px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.18520834;"
731
+ x="43.709164"
732
+ y="255.8875"
733
+ id="text919"><tspan
734
+ sodipodi:role="line"
735
+ id="tspan917"
736
+ x="43.709164"
737
+ y="255.8875"
738
+ style="font-size:2.96333332px;stroke-width:0.18520834;">* non-blocking area</tspan><tspan
739
+ sodipodi:role="line"
740
+ x="43.709164"
741
+ y="259.90033"
742
+ style="font-size:2.96333332px;stroke-width:0.18520834;"
743
+ id="tspan921">* synchronized</tspan><tspan
744
+ sodipodi:role="line"
745
+ x="43.709164"
746
+ y="263.91318"
747
+ style="font-size:2.96333332px;stroke-width:0.18520834;"
748
+ id="tspan4773">* event driven</tspan></text>
749
+ <text
750
+ xml:space="preserve"
751
+ style="font-style:normal;font-weight:normal;font-size:2.96333332px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.18520834;"
752
+ x="34.263542"
753
+ y="175.32187"
754
+ id="text1060"><tspan
755
+ sodipodi:role="line"
756
+ x="34.263542"
757
+ y="175.32187"
758
+ style="font-size:2.96333332px;stroke-width:0.18520834;"
759
+ id="tspan4765">* blocking, thread-safe area</tspan></text>
760
+ </g>
761
+ </svg>