sekka 1.5.3 → 1.5.4

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
  SHA1:
3
- metadata.gz: b43563da2b684d228330de35e6582f3b388d97cf
4
- data.tar.gz: 94bbfa6451a27d3016f061155216d7be20f24ebe
3
+ metadata.gz: fdda3d8bdbf3dd519dfc6d3eeb202f0968bd3f0f
4
+ data.tar.gz: ef1a33ab474b7bb5deebaeb49e8ab32e7deaa3ee
5
5
  SHA512:
6
- metadata.gz: 8c7ad5fcf361ef7da668c3ac42a68f86fde7f50966a54663c7662b409848170202a1bf306a9fc36c34e1cff02e0b394cc75a1bc3f022c73c72a458b09a81c270
7
- data.tar.gz: 13313c3c2e658be84bea426b89117a8da7734b7caac50f40655654e4ddcb8ddadfea3cfb83b0f506c59803d338ad932db71acd119dd002a6f03f2a09ab6c33bd
6
+ metadata.gz: 124923840d365584238349826ad008200173fa8c43809a8aa351e87b4c9d205f35ffc01dc7ee864a5273222670fd15e819372c64e4b39ce1c7edcd1f9a635105
7
+ data.tar.gz: e29b0b82b8eb6d4a850e4204a768025972c20a4f0c3e675bec835210ecae4da751469ddf2d05f544ec711d552c707bc0c1f0f8842ba900d95b0aceda5b5d2b36
data/VERSION.yml CHANGED
@@ -1,4 +1,4 @@
1
1
  ---
2
2
  :major: 1
3
3
  :minor: 5
4
- :patch: 3
4
+ :patch: 4
@@ -0,0 +1,508 @@
1
+ ;;; concurrent.el --- Concurrent utility functions for emacs lisp
2
+
3
+ ;; Copyright (C) 2010, 2011, 2012 SAKURAI Masashi
4
+
5
+ ;; Author: SAKURAI Masashi <m.sakurai at kiwanami.net>
6
+ ;; Version: 0.3.1
7
+ ;; Keywords: deferred, async, concurrent
8
+ ;; Package-Requires: ((deferred "0.3.1"))
9
+ ;; URL: https://github.com/kiwanami/emacs-deferred/blob/master/README-concurrent.markdown
10
+
11
+ ;; This program is free software; you can redistribute it and/or modify
12
+ ;; it under the terms of the GNU General Public License as published by
13
+ ;; the Free Software Foundation, either version 3 of the License, or
14
+ ;; (at your option) any later version.
15
+
16
+ ;; This program is distributed in the hope that it will be useful,
17
+ ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
18
+ ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19
+ ;; GNU General Public License for more details.
20
+
21
+ ;; You should have received a copy of the GNU General Public License
22
+ ;; along with this program. If not, see <http://www.gnu.org/licenses/>.
23
+
24
+ ;;; Commentary:
25
+
26
+ ;; 'concurrent.el' is a higher level library for concurrent tasks
27
+ ;; based on 'deferred.el'. This library has following features:
28
+ ;;
29
+ ;; - Generator
30
+ ;; - Green thread
31
+ ;; - Semaphore
32
+ ;; - Dataflow
33
+ ;; - Signal/Channel
34
+
35
+ (require 'cl)
36
+
37
+ (require 'deferred)
38
+
39
+ (defvar cc:version nil "version number")
40
+ (setq cc:version "0.3")
41
+
42
+ ;;; Code:
43
+
44
+
45
+
46
+ (defmacro cc:aif (test-form then-form &rest else-forms)
47
+ (declare (debug (form form &rest form)))
48
+ `(let ((it ,test-form))
49
+ (if it ,then-form ,@else-forms)))
50
+ (put 'cc:aif 'lisp-indent-function 2)
51
+
52
+ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
53
+ ;; Generator
54
+
55
+ (defun cc:generator-replace-yield (tree)
56
+ "[internal] Replace `yield' symbols to calling a function in TREE."
57
+ (let (ret)
58
+ (loop for i in tree
59
+ do (cond
60
+ ((eq i 'yield)
61
+ (push 'funcall ret)
62
+ (push i ret))
63
+ ((listp i)
64
+ (push (cc:generator-replace-yield i) ret))
65
+ (t
66
+ (push i ret))))
67
+ (nreverse ret)))
68
+
69
+ (defun cc:generator-line (chain line)
70
+ "[internal] Return a macro expansion to execute the sexp LINE
71
+ asynchronously."
72
+ (cond
73
+ ;; function object
74
+ ((functionp line)
75
+ `(setq ,chain (deferred:nextc ,chain ,line)))
76
+ ;; while loop form
77
+ ((eq 'while (car line))
78
+ (let ((condition (cadr line))
79
+ (body (cddr line)))
80
+ `(setq ,chain
81
+ (deferred:nextc ,chain
82
+ (deferred:lambda (x)
83
+ (if ,condition
84
+ (deferred:nextc
85
+ (progn
86
+ ,@(cc:generator-replace-yield body)) self)))))))
87
+ ;; statement
88
+ (t
89
+ `(setq ,chain
90
+ (deferred:nextc ,chain
91
+ (deferred:lambda (x) ,(cc:generator-replace-yield line)))))))
92
+
93
+ (defmacro cc:generator (callback &rest body)
94
+ "Create a generator object. If BODY has `yield' symbols, it
95
+ means calling callback function CALLBACK."
96
+ (let ((chain (gensym))
97
+ (cc (gensym))
98
+ (waiter (gensym)))
99
+ `(lexical-let*
100
+ (,chain
101
+ (,cc ,callback)
102
+ (,waiter (deferred:new))
103
+ (yield (lambda (x) (funcall ,cc x) ,waiter)))
104
+ (setq ,chain ,waiter)
105
+ ,@(loop for i in body
106
+ collect
107
+ (cc:generator-line chain i))
108
+ (lambda () (deferred:callback ,waiter)))))
109
+
110
+
111
+
112
+ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
113
+ ;; Thread
114
+
115
+ (defun cc:thread-line (wait-time chain line)
116
+ "[internal] Return a macro expansion to execute the sexp LINE asynchronously.
117
+ WAIT-TIME is an interval time between tasks.
118
+ CHAIN is the previous deferred task."
119
+ (cond
120
+ ;; function object
121
+ ((functionp line)
122
+ `(setq ,chain (deferred:nextc ,chain ,line)))
123
+ ;; while loop form
124
+ ((eq 'while (car line))
125
+ (let ((condition (cadr line))
126
+ (body (cddr line))
127
+ (retsym (gensym)))
128
+ `(setq ,chain
129
+ (deferred:nextc ,chain
130
+ (deferred:lambda (x)
131
+ (if ,condition
132
+ (deferred:nextc
133
+ (let ((,retsym (progn ,@body)))
134
+ (if (deferred-p ,retsym) ,retsym
135
+ (deferred:wait ,wait-time)))
136
+ self)))))))
137
+ ;; statement
138
+ (t
139
+ `(setq ,chain
140
+ (deferred:nextc ,chain
141
+ (lambda (x) ,line))))))
142
+
143
+ (defmacro cc:thread (wait-time-msec &rest body)
144
+ "Return a thread object."
145
+ (let ((chain (gensym))
146
+ (dstart (gensym)))
147
+ `(lexical-let*
148
+ (,chain
149
+ (,dstart (deferred:new)))
150
+ (setq ,chain ,dstart)
151
+ ,@(loop for i in body
152
+ collect
153
+ (cc:thread-line wait-time-msec chain i))
154
+ (deferred:callback ,dstart))))
155
+ (put 'cc:thread 'lisp-indent-function 1)
156
+
157
+
158
+
159
+ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
160
+ ;; Semaphore
161
+
162
+ (defstruct cc:semaphore max-permits permits waiting-deferreds)
163
+
164
+ (defun cc:semaphore-create(permits-num)
165
+ "Return a semaphore object with PERMITS-NUM permissions."
166
+ (make-cc:semaphore :max-permits permits-num :permits permits-num))
167
+
168
+ (defun cc:semaphore-acquire(semaphore)
169
+ "Acquire an execution permission and return deferred object to chain.
170
+ If this semaphore object has permissions, the subsequent deferred
171
+ task is executed immediately. If this semaphore object has no
172
+ permissions, the subsequent deferred task is blocked. After the
173
+ permission is returned, the task is executed."
174
+ (cond
175
+ ((< 0 (cc:semaphore-permits semaphore))
176
+ (decf (cc:semaphore-permits semaphore))
177
+ (deferred:succeed))
178
+ (t
179
+ (let ((d (deferred:new)))
180
+ (push d (cc:semaphore-waiting-deferreds semaphore))
181
+ d))))
182
+
183
+ (defun cc:semaphore-release(semaphore)
184
+ "Release an execution permission. The programmer is responsible to return the permissions."
185
+ (when (<= (cc:semaphore-max-permits semaphore)
186
+ (cc:semaphore-permits semaphore))
187
+ (error "Too many calling semaphore-release. [max:%s <= permits:%s]"
188
+ (cc:semaphore-max-permits semaphore)
189
+ (cc:semaphore-permits semaphore)))
190
+ (let ((waiting-deferreds
191
+ (cc:semaphore-waiting-deferreds semaphore)))
192
+ (cond
193
+ (waiting-deferreds
194
+ (let* ((d (car (last waiting-deferreds))))
195
+ (setf (cc:semaphore-waiting-deferreds semaphore)
196
+ (nbutlast waiting-deferreds))
197
+ (deferred:callback-post d)))
198
+ (t
199
+ (incf (cc:semaphore-permits semaphore)))))
200
+ semaphore)
201
+
202
+ (defun cc:semaphore-with (semaphore body-func &optional error-func)
203
+ "Execute the task BODY-FUNC asynchronously with the semaphore block."
204
+ (lexical-let ((semaphore semaphore))
205
+ (deferred:try
206
+ (deferred:nextc (cc:semaphore-acquire semaphore) body-func)
207
+ :catch
208
+ error-func
209
+ :finally
210
+ (lambda (x) (cc:semaphore-release semaphore)))))
211
+ (put 'cc:semaphore-with 'lisp-indent-function 1)
212
+
213
+ (defun cc:semaphore-release-all (semaphore)
214
+ "Release all permissions for resetting the semaphore object.
215
+ If the semaphore object has some blocked tasks, this function
216
+ return a list of the tasks and clear the list of the blocked
217
+ tasks in the semaphore object."
218
+ (setf (cc:semaphore-permits semaphore)
219
+ (cc:semaphore-max-permits semaphore))
220
+ (let ((ds (cc:semaphore-waiting-deferreds semaphore)))
221
+ (when ds
222
+ (setf (cc:semaphore-waiting-deferreds semaphore) nil))
223
+ ds))
224
+
225
+ (defun cc:semaphore-interrupt-all (semaphore)
226
+ "Clear the list of the blocked tasks in the semaphore and return a deferred object to chain.
227
+ This function is used for the interruption cases."
228
+ (when (cc:semaphore-waiting-deferreds semaphore)
229
+ (setf (cc:semaphore-waiting-deferreds semaphore) nil)
230
+ (setf (cc:semaphore-permits semaphore) 0))
231
+ (cc:semaphore-acquire semaphore))
232
+
233
+
234
+
235
+ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
236
+ ;; Signal / Channel
237
+
238
+ (defun cc:signal-channel (&optional name parent-channel)
239
+ "Create a channel.
240
+ NAME is a channel name for debug.
241
+ PARENT-CHANNEL is an upstream channel. The observers of this channel can receive the upstream signals.
242
+ In the case of using the function `cc:signal-send', the observers of the upstream channel can not receive the signals of this channel. The function `cc:signal-send-global' can send a signal to the upstream channels from the downstream channels."
243
+ (lexical-let
244
+ ((ch (cons
245
+ (or name (format "signal%s" (deferred:uid))) ; name for debug
246
+ (cons
247
+ parent-channel ; parent-channel
248
+ nil)))) ; observers
249
+ (when parent-channel
250
+ (cc:signal-connect
251
+ parent-channel
252
+ t (lambda (event)
253
+ (destructuring-bind
254
+ (event-name event-args) event
255
+ (apply 'cc:signal-send
256
+ ch event-name event-args)))))
257
+ ch))
258
+
259
+ (defmacro cc:signal-name (ch)
260
+ "[internal] Return signal name."
261
+ `(car ,ch))
262
+
263
+ (defmacro cc:signal-parent-channel (ch)
264
+ "[internal] Return parent channel object."
265
+ `(cadr ,ch))
266
+
267
+ (defmacro cc:signal-observers (ch)
268
+ "[internal] Return observers."
269
+ `(cddr ,ch))
270
+
271
+ (defun cc:signal-connect (channel event-sym &optional callback)
272
+ "Append an observer for EVENT-SYM of CHANNEL and return a deferred object.
273
+ If EVENT-SYM is `t', the observer receives all signals of the channel.
274
+ If CALLBACK function is given, the deferred object executes the
275
+ CALLBACK function asynchronously. One can connect subsequent
276
+ tasks to the returned deferred object."
277
+ (let ((d (if callback
278
+ (deferred:new callback)
279
+ (deferred:new))))
280
+ (push (cons event-sym d)
281
+ (cc:signal-observers channel))
282
+ d))
283
+
284
+ (defun cc:signal-send (channel event-sym &rest args)
285
+ "Send a signal to CHANNEL. If ARGS values are given, observers can get the values by following code: (lambda (event) (destructuring-bind (event-sym (args)) event ... )). "
286
+ (let ((observers (cc:signal-observers channel))
287
+ (event (list event-sym args)))
288
+ (loop for i in observers
289
+ for name = (car i)
290
+ for d = (cdr i)
291
+ if (or (eq event-sym name) (eq t name))
292
+ do (deferred:callback-post d event))))
293
+
294
+ (defun cc:signal-send-global (channel event-sym &rest args)
295
+ "Send a signal to the most upstream channel. "
296
+ (cc:aif (cc:signal-parent-channel channel)
297
+ (apply 'cc:signal-send-global it event-sym args)
298
+ (apply 'cc:signal-send channel event-sym args)))
299
+
300
+
301
+ (defun cc:signal-disconnect (channel deferred)
302
+ "Remove the observer object DEFERRED from CHANNEL and return
303
+ the removed deferred object. "
304
+ (let ((observers (cc:signal-observers channel)) deleted)
305
+ (setf
306
+ (cc:signal-observers channel) ; place
307
+ (loop for i in observers
308
+ for d = (cdr i)
309
+ unless (eq d deferred)
310
+ collect i
311
+ else
312
+ do (push i deleted)))
313
+ deleted))
314
+
315
+ (defun cc:signal-disconnect-all (channel)
316
+ "Remove all observers."
317
+ (setf
318
+ (cc:signal-observers channel) ; place
319
+ nil))
320
+
321
+
322
+
323
+
324
+ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
325
+ ;; Dataflow
326
+
327
+ ;; Dataflow variable entry
328
+ (defstruct cc:dataflow key (value 'cc:dataflow-undefine) deferred-list)
329
+
330
+ (defun cc:dataflow-undefine-p (obj)
331
+ "[internal] If the variable entry is not bound, return `t'."
332
+ (eq 'cc:dataflow-undefine (cc:dataflow-value obj)))
333
+
334
+ (defun cc:dataflow-environment (&optional parent-env test-func channel)
335
+ "Create a dataflow environment.
336
+ PARENT-ENV is the default environment. If this environment doesn't have the entry A and the parent one has the entry A, this environment can return the entry A. One can override the entry, setting another entry A to this environment.
337
+ TEST-FUNC is a test function that compares the entry keys. The default function is `equal'.
338
+ CHANNEL is a channel object that sends signals of variable events. Observers can receive following signals:
339
+ -get-first : the fist referrer is waiting for binding,
340
+ -get-waiting : another referrer is waiting for binding,
341
+ -set : a value is bound,
342
+ -get : returned a bound value,
343
+ -clear : cleared one entry,
344
+ -clear-all : cleared all entries.
345
+ "
346
+ (let ((this (list parent-env
347
+ (or test-func 'equal)
348
+ (or channel
349
+ (cc:signal-channel
350
+ 'dataflow
351
+ (and parent-env
352
+ (cc:dataflow-channel parent-env)))))))
353
+ (cc:dataflow-init-connect this)
354
+ this))
355
+
356
+ (defun cc:dataflow-init-connect (df)
357
+ "[internal] Initialize the channel object."
358
+ (lexical-let ((df df))
359
+ (cc:dataflow-connect
360
+ df 'set
361
+ (lambda (args)
362
+ (destructuring-bind (event (key)) args
363
+ (let* ((obj (cc:dataflow-get-object-for-value df key))
364
+ (value (and obj (cc:dataflow-value obj))))
365
+ (when obj
366
+ (loop for i in (cc:aif (cc:dataflow-get-object-for-deferreds df key)
367
+ (cc:dataflow-deferred-list it) nil)
368
+ do (deferred:callback-post i value))
369
+ (setf (cc:dataflow-deferred-list obj) nil))))))))
370
+
371
+ (defmacro cc:dataflow-parent-environment (df)
372
+ "[internal] Return the parent environment."
373
+ `(car ,df))
374
+
375
+ (defmacro cc:dataflow-test (df)
376
+ "[internal] Return the test function."
377
+ `(cadr ,df))
378
+
379
+ (defmacro cc:dataflow-channel (df)
380
+ "[internal] Return the channel object."
381
+ `(caddr ,df))
382
+
383
+ (defmacro cc:dataflow-list (df)
384
+ "[internal] Return the list of deferred object which are waiting for value binding."
385
+ `(cdddr ,df))
386
+
387
+ (defun cc:dataflow-get-object-for-value (df key)
388
+ "[internal] Return an entry object that is indicated by KEY.
389
+ If the environment DF doesn't have the entry and the parent one has the entry, this function returns the entry of the parent environment. This function doesn't affect the waiting list."
390
+ (or
391
+ (loop for i in (cc:dataflow-list df)
392
+ with test = (cc:dataflow-test df)
393
+ if (and (funcall test key (cc:dataflow-key i))
394
+ (not (cc:dataflow-undefine-p i)))
395
+ return i)
396
+ (deferred:aand
397
+ (cc:dataflow-parent-environment df)
398
+ (cc:dataflow-get-object-for-value it key))))
399
+
400
+ (defun cc:dataflow-get-object-for-deferreds (df key)
401
+ "[internal] Return a list of the deferred objects those are waiting for value binding.
402
+ This function doesn't affect the waiting list and doesn't refer the parent environment."
403
+ (loop for i in (cc:dataflow-list df)
404
+ with test = (cc:dataflow-test df)
405
+ if (funcall test key (cc:dataflow-key i))
406
+ return i))
407
+
408
+ (defun cc:dataflow-connect (df event-sym &optional callback)
409
+ "Append an observer for EVENT-SYM of the channel of DF and return a deferred object.
410
+ See the docstring of `cc:dataflow-environment' for details."
411
+ (cc:signal-connect (cc:dataflow-channel df) event-sym callback))
412
+
413
+ (defun cc:dataflow-signal (df event &optional arg)
414
+ "[internal] Send a signal to the channel of DF."
415
+ (cc:signal-send (cc:dataflow-channel df) event arg))
416
+
417
+ (defun cc:dataflow-get (df key)
418
+ "Return a deferred object that can refer the value which is indicated by KEY.
419
+ If DF has the entry that bound value, the subsequent deferred task is executed immediately.
420
+ If not, the task is deferred till a value is bound."
421
+ (let ((obj (cc:dataflow-get-object-for-value df key)))
422
+ (cond
423
+ ((and obj (cc:dataflow-value obj))
424
+ (cc:dataflow-signal df 'get key)
425
+ (deferred:succeed (cc:dataflow-value obj)))
426
+ (t
427
+ (setq obj (cc:dataflow-get-object-for-deferreds df key))
428
+ (unless obj
429
+ (setq obj (make-cc:dataflow :key key))
430
+ (push obj (cc:dataflow-list df))
431
+ (cc:dataflow-signal df 'get-first key))
432
+ (let ((d (deferred:new)))
433
+ (push d (cc:dataflow-deferred-list obj))
434
+ (cc:dataflow-signal df 'get-waiting key)
435
+ d)))))
436
+
437
+ (defun cc:dataflow-get-sync (df key)
438
+ "Return the value which is indicated by KEY synchronously.
439
+ If the environment DF doesn't have an entry of KEY, this function returns nil."
440
+ (let ((obj (cc:dataflow-get-object-for-value df key)))
441
+ (and obj (cc:dataflow-value obj))))
442
+
443
+ (defun cc:dataflow-set (df key value)
444
+ "Bind the VALUE to KEY in the environment DF.
445
+ If DF already has the bound entry of KEY, this function throws an error signal.
446
+ VALUE can be nil as a value."
447
+ (let ((obj (cc:dataflow-get-object-for-deferreds df key)))
448
+ (cond
449
+ ((and obj (not (cc:dataflow-undefine-p obj)))
450
+ ;; overwrite!
451
+ (error "Can not set a dataflow value. The key [%s] has already had a value. NEW:[%s] OLD:[%s]" key value (cc:dataflow-value obj)))
452
+ (obj
453
+ (setf (cc:dataflow-value obj) value))
454
+ (t
455
+ ;; just value arrived
456
+ (push (make-cc:dataflow :key key :value value)
457
+ (cc:dataflow-list df))))
458
+ ;; value arrived and start deferred objects
459
+ (cc:dataflow-signal df 'set key)
460
+ value))
461
+
462
+ (defun cc:dataflow-clear (df key)
463
+ "Clear the entry which is indicated by KEY.
464
+ This function does nothing for the waiting deferred objects."
465
+ (cc:dataflow-signal df 'clear key)
466
+ (setf (cc:dataflow-list df)
467
+ (loop for i in (cc:dataflow-list df)
468
+ with test = (cc:dataflow-test df)
469
+ unless (funcall test key (cc:dataflow-key i))
470
+ collect i)))
471
+
472
+ (defun cc:dataflow-get-avalable-pairs (df)
473
+ "Return an available key-value alist in the environment DF and the parent ones."
474
+ (append
475
+ (loop for i in (cc:dataflow-list df)
476
+ for key = (cc:dataflow-key i)
477
+ for val = (cc:dataflow-value i)
478
+ unless (cc:dataflow-undefine-p i) collect (cons key val))
479
+ (deferred:aand
480
+ (cc:dataflow-parent-environment df)
481
+ (cc:dataflow-get-avalable-pairs it))))
482
+
483
+ (defun cc:dataflow-get-waiting-keys (df)
484
+ "Return a list of keys which have waiting deferred objects in the environment DF and the parent ones."
485
+ (append
486
+ (loop for i in (cc:dataflow-list df)
487
+ for key = (cc:dataflow-key i)
488
+ for val = (cc:dataflow-value i)
489
+ if (cc:dataflow-undefine-p i) collect key)
490
+ (deferred:aand
491
+ (cc:dataflow-parent-environment df)
492
+ (cc:dataflow-get-waiting-keys it))))
493
+
494
+ (defun cc:dataflow-clear-all (df)
495
+ "Clear all entries in the environment DF.
496
+ This function does nothing for the waiting deferred objects."
497
+ (cc:dataflow-signal df 'clear-all)
498
+ (setf (cc:dataflow-list df) nil))
499
+
500
+
501
+ (provide 'concurrent)
502
+
503
+ ;; Local Variables:
504
+ ;; byte-compile-warnings: (not cl-functions)
505
+ ;; End:
506
+
507
+ ;;; concurrent.el ends here
508
+