source: project/wiki/man/4/Unit srfi-18 @ 25965

Last change on this file since 25965 was 25965, checked in by svnwiki, 9 years ago

Anonymous wiki edit for IP [77.107.164.131]: Final changes.

File size: 39.3 KB
Line 
1[[tags: manual]]
2
3[[toc:]]
4
5== Unit srfi-18
6
7A simple multithreading package, largely following the specification
8of [[http://srfi.schemers.org/srfi-18/srfi-18.html|SRFI-18]].  This
9document contains the core of the SRFI-18 documentation as well as
10information on Chicken deviations from the spec.
11
12SRFI-18 defines the following multithreading datatypes:
13
14* Thread
15* Mutex
16* Condition variable
17* Time
18
19It also defines a mechanism to handle exceptions and some multithreading
20exception datatypes.
21
22== Chicken implementation
23
24=== Notes
25
26* {{thread-start!}} accepts a thunk (a zero argument procedure) as argument, which is equivalent to {{(thread-start! (make-thread THUNK))}}.
27
28* {{thread-sleep!}} accepts a seconds real number value in addition to a time object.
29
30* When an uncaught exception (i.e. an error) is signalled in a thread other than the primordial thread and warnings are enabled (see: {{enable-warnings}}, then a warning message is written to the port that is the value of {{(current-error-port)}}.
31
32* Blocking I/O will block all threads, except for some socket operations (see the section about the {{tcp}} unit). An exception is the read-eval-print loop on UNIX platforms: waiting for input will not block other threads, provided the current input port reads input from a console.
33
34* It is generally not a good idea for one thread to call a continuation created by another thread, if {{dynamic-wind}} is involved.
35
36* When more than one thread compete for the current time-slice, the thread that was waiting first will become the next runnable thread.
37
38* The dynamic environment of a thread consists of the following state:
39
40** The current input-, output- and error-port
41
42** The current exception handler
43
44** The values of all current parameters (created by {{make-parameter}})
45
46** Any pending {{dynamic-wind}} thunks.
47
48* When an error is triggered inside the execution context of a thread, the default exception-handler will simply terminate the thread (and store the error condition for later use). Pending {{dynamic-wind}} thunks will ''not'' be invoked. Use a custom exception handler for the thread in that case.
49
50=== Procedures
51
52The following procedures are provided in addition to the procedures defined in SRFI-18.
53
54<procedure>(thread-signal! THREAD X)</procedure>
55
56This will cause {{THREAD}} to signal the condition {{X}} once it is scheduled
57for execution. After signalling the condition, the thread continues with its normal
58execution.
59
60<procedure>(thread-quantum THREAD)</procedure>
61
62Returns the quantum of {{THREAD}}, which is an exact integer
63specifying the approximate time-slice of the thread in milliseconds.
64
65<procedure>(thread-quantum-set! THREAD QUANTUM)</procedure>
66
67Sets the quantum of {{THREAD}} to {{QUANTUM}}.
68
69<procedure>(thread-suspend! THREAD)</procedure>
70
71Suspends the execution of {{THREAD}} until resumed.
72
73<procedure>(thread-resume! THREAD)</procedure>
74
75Readies the suspended thread {{THREAD}}.
76
77<procedure>(thread-wait-for-i/o! FD [MODE])</procedure>
78
79Suspends the current thread until input ({{MODE}} is {{#:input}}), output ({{MODE}} is {{#:output}})
80or both ({{MODE}} is {{#:all}}) is available. {{FD}} should be a file-descriptor (not a port!) open
81for input or output, respectively.
82
83<procedure>(thread-state thread)</procedure><br>
84
85Returns information about the state of the {{thread}}. The possible results
86are:
87
88
89* '''symbol {{created}}''': the {{thread}} is in the created state
90* '''symbol {{ready}}''': the {{thread}} is in the ready state
91* '''symbol {{running}}''': the {{thread}} is in the running state
92* '''symbol {{blocked}}''': the {{thread}} is in the blocked state
93* '''symbol {{suspended}}''': the {{thread}} is in the suspended state
94* '''symbol {{sleeping}}''': the {{thread}} is in the sleeping state
95* '''symbol {{terminated}}''': the {{thread}} is in the terminated state
96* '''symbol {{dead}}''': the {{thread}} is in the dead state
97
98
99== SRFI-18 specification
100
101The thread system provides the following data types:
102
103* Thread (a virtual processor which shares object space with all other threads)
104* Mutex (a mutual exclusion device, also known as a lock and binary semaphore)
105* Condition variable (a set of blocked threads)
106* Time (an absolute point on the time line)
107
108Some multithreading exception datatypes are also specified, and a general
109mechanism for handling exceptions.
110
111=== Background information
112
113==== Threads
114
115A "running" thread is a thread that is currently executing. There can be
116more than one running thread on a multiprocessor machine. A "runnable"
117thread is a thread that is ready to execute or running. A thread is
118"blocked" if it is waiting for a mutex to become unlocked, an I/O operation
119to become possible, the end of a "sleep" period, etc. A "new" thread is a
120thread that has not yet become runnable. A new thread becomes runnable when
121it is started. A "terminated" thread is a thread that can no longer become
122runnable (but "deadlocked" threads are not considered terminated). The
123only valid transitions between the thread states are from new to runnable,
124between runnable and blocked, and from any state to terminated:
125
126
127                          unblock
128        start            <-------
129   NEW -------> RUNNABLE -------> BLOCKED
130     \             |      block  /
131      \            v            /
132       +-----> TERMINATED <----+
133
134
135Each thread has a "specific" field which can be used in an application
136specific way to associate data with the thread (some thread systems call
137this "thread local storage").
138
139==== Mutexes
140
141A mutex can be in one of four states: locked (either owned or not owned)
142and unlocked (either abandoned or not abandoned). An attempt to lock
143a mutex only succeeds if the mutex is in an unlocked state, otherwise
144the current thread must wait. A mutex in the locked/owned state has an
145associated "owner" thread, which by convention is the thread that is
146responsible for unlocking the mutex (this case is typical of critical
147sections implemented as "lock mutex, perform operation, unlock mutex"). A
148mutex in the locked/not-owned state is not linked to a particular thread.
149A mutex becomes locked when a thread locks it using the {{mutex-lock!}}
150primitive. A mutex becomes unlocked/abandoned when the owner of a
151locked/owned mutex terminates. A mutex becomes unlocked/not-abandoned
152when a thread unlocks it using the {{mutex-unlock!}} primitive. The mutex
153primitives specified in this SRFI do not implement "recursive" mutex
154semantics; an attempt to lock a mutex that is locked implies that the
155current thread must wait even if the mutex is owned by the current thread
156(this can lead to a deadlock if no other thread unlocks the mutex).
157
158Each mutex has a "specific" field which can be used in an application
159specific way to associate data with the mutex.
160
161
162==== Condition variables
163
164A condition variable represents a set of blocked threads. These blocked
165threads are waiting for a certain condition to become true. When a thread
166modifies some program state that might make the condition true, the thread
167unblocks some number of threads (one or all depending on the primitive
168used) so they can check the value of the condition. This allows complex
169forms of interthread synchronization to be expressed more conveniently than
170with mutexes alone.
171
172Each condition variable has a "specific" field which can be used in an
173application specific way to associate data with the condition variable.
174
175
176==== Fairness
177
178In various situations the scheduler must select one thread from a set of
179threads (e.g. which thread to run when a running thread blocks or expires
180its quantum, which thread to unblock when a mutex unlocks or a condition
181variable is signaled). The constraints on the selection process determine
182the scheduler's "fairness". Typically the selection depends on the order in
183which threads become runnable or blocked and on some "priority" attached to
184the threads.
185
186Because we do not wish to preclude extensions to this SRFI (such as for
187real-time multithreading) that require specific fairness constraints, there
188are no fairness constraints imposed by this SRFI. It is expected however
189that implementations of Scheme that support this SRFI will document the
190fairness constraints they provide.
191
192
193==== Memory coherency and lack of atomicity
194
195Read and write operations on the store (such as reading and writing a
196variable, an element of a vector or a string) are not required to be
197atomic. It is an error for a thread to write a location in the store
198while some other thread reads or writes that same location. It is the
199responsibility of the application to avoid write/read and write/write races
200through appropriate uses of the synchronization primitives.
201
202Concurrent reads and writes to ports are allowed. It is the responsibility
203of the implementation to serialize accesses to a given port using the
204appropriate synchronization primitives.
205
206
207==== Dynamic environments, continuations and {{dynamic-wind}}
208
209The "dynamic environment" is a structure which allows the system to find
210the value returned by {{current-input-port}}, {{current-output-port}},
211etc. The procedures {{with-input-from-file}}, {{with-output-to-file}},
212etc extend the dynamic environment to produce a new dynamic environment
213which is in effect for the duration of the call to the thunk passed as the
214last argument. Some Scheme systems generalize the dynamic environment by
215providing procedures and special forms to define new "dynamic variables"
216and bind them in the dynamic environment (e.g. {{make-parameter}} and
217{{parameterize}}).
218
219Each thread has its own dynamic environment. When a thread's dynamic
220environment is extended this does not affect the dynamic environment
221of other threads. When a thread creates a continuation, the thread's
222dynamic environment and the {{dynamic-wind}} stack are saved within
223the continuation (an alternate but equivalent point of view is that the
224{{dynamic-wind}} stack is part of the dynamic environment). When this
225continuation is invoked the required {{dynamic-wind}} before and after
226thunks are called and the saved dynamic environment is reinstated as the
227dynamic environment of the current thread. During the call to each required
228{{dynamic-wind}} before and after thunk, the dynamic environment and the
229{{dynamic-wind}} stack in effect when the corresponding {{dynamic-wind}}
230was executed are reinstated. Note that this specification clearly defines
231the semantics of calling {{call-with-current-continuation}} or invoking a
232continuation within a before or after thunk. The semantics are well defined
233even when a continuation created by another thread is invoked. Below is an
234example exercising the subtleties of this semantics.
235
236
237     (with-output-to-file
238      "foo"
239      (lambda ()
240        (let ((k (call-with-current-continuation
241                  (lambda (exit)
242                    (with-output-to-file
243                     "bar"
244                     (lambda ()
245                       (dynamic-wind
246                        (lambda () (write '(b1)))
247                        (lambda ()
248                          (let ((x (call-with-current-continuation
249                                    (lambda (cont) (exit cont)))))
250                            (write '(t1))
251                            x))
252                        (lambda () (write '(a1))))))))))
253          (if k
254              (dynamic-wind
255               (lambda () (write '(b2)))
256               (lambda ()
257                 (with-output-to-file
258                  "baz"
259                  (lambda ()
260                    (write '(t2))
261                    ; go back inside (with-output-to-file "bar" ...)
262                    (k #f))))
263               (lambda () (write '(a2))))))))
264
265In an implementation of Scheme where {{with-output-to-file}} only closes
266the port it opened when the thunk returns normally, then the following
267actions will occur: {{(b1)(a1)}} is written to "bar", {{(b2)}} is written
268to "foo", {{(t2)}} is written to "baz", {{(a2)}} is written to "foo", and
269{{(b1)(t1)(a1)}} is written to "bar".
270
271When the scheduler stops the execution of a running thread T1 (whether
272because it blocked, expired its quantum, was terminated, etc) and then
273resumes the execution of a thread T2, there is in a sense a transfer of
274control between T1's current continuation and the continuation of T2. This
275transfer of control by the scheduler does not cause any {{dynamic-wind}}
276before and after thunks to be called. It is only when a thread itself
277transfers control to a continuation that {{dynamic-wind}} before and after
278thunks are called.
279
280
281==== Time objects and timeouts
282
283A time object represents a point on the time line. Its resolution is
284implementation dependent (implementations are encouraged to implement at
285least millisecond resolution so that precise timing is possible). Using
286{{time->seconds}} and {{seconds->time}}, a time object can be converted
287to and from a real number which corresponds to the number of seconds from
288a reference point on the time line. The reference point is implementation
289dependent and does not change for a given execution of the program (e.g.
290the reference point could be the time at which the program started).
291
292All synchronization primitives which take a timeout parameter accept three
293types of values as a timeout, with the following meaning:
294
295
296* a time object represents an absolute point in time
297* an exact or inexact real number represents a relative time in seconds from the moment the primitive was called
298* {{#f}} means that there is no timeout
299
300When a timeout denotes the current time or a time in the past, the
301synchronization primitive claims that the timeout has been reached only
302after the other synchronization conditions have been checked. Moreover the
303thread remains running (it does not enter the blocked state). For example,
304{{(mutex-lock! m 0)}} will lock mutex {{m}} and return {{#t}} if {{m}} is
305currently unlocked, otherwise {{#f}} is returned because the timeout is
306reached.
307
308
309==== Primitives and exceptions
310
311When one of the primitives defined in this SRFI raises an exception defined
312in this SRFI, the exception handler is called with the same continuation
313as the primitive (i.e. it is a tail call to the exception handler). This
314requirement avoids having to use {{call-with-current-continuation}} to get
315the same effect in some situations.
316
317
318==== Primordial thread
319
320The execution of a program is initially under the control of a single
321thread known as the "primordial thread". The primordial thread has an
322unspecified name, specific field, dynamic environment, {{dynamic-wind}}
323stack, and exception handler. All threads are terminated when the
324primordial thread terminates (normally or not).
325
326
327=== Procedures
328
329<procedure>(current-thread)</procedure><br>
330
331Returns the current thread.
332
333
334     (eq? (current-thread) (current-thread))  ==>  #t
335
336
337<procedure>(thread? obj)</procedure><br>
338
339Returns {{#t}} if {{obj}} is a thread, otherwise returns {{#f}}.
340
341
342     (thread? (current-thread))  ==>  #t
343     (thread? 'foo)              ==>  #f
344
345<procedure>(make-thread thunk [name])</procedure><br>
346
347Returns a new thread. This thread is not automatically made runnable
348(the procedure {{thread-start!}} must be used for this).
349
350A thread has the following fields: name, specific, end-result,
351end-exception, and a list of locked/owned mutexes it owns. The
352thread's execution consists of a call to ''thunk'' with the "initial
353continuation". This continuation causes the (then) current thread to
354store the result in its end-result field, abandon all mutexes it owns,
355and finally terminate. The {{dynamic-wind}} stack of the initial
356continuation is empty. The optional {{name}} is an arbitrary Scheme
357object which identifies the thread (useful for debugging); it defaults
358to an unspecified value. The specific field is set to an unspecified
359value.
360
361The thread inherits the dynamic environment from the current
362thread. Moreover, in this dynamic environment the exception handler is
363bound to the "initial exception handler" which is a unary procedure
364which causes the (then) current thread to store in its end-exception
365field an "uncaught exception" object whose "reason" is the argument of
366the handler, abandon all mutexes it owns, and finally terminate.
367
368     (make-thread (lambda () (write 'hello)))  ==>  ''a thread''
369
370<procedure>(thread-name thread)</procedure><br>
371
372Returns the name of the {{thread}}.
373
374
375     (thread-name (make-thread (lambda () #f) 'foo))  ==>  foo
376
377<procedure>(thread-specific thread)</procedure><br>
378
379Returns the content of the {{thread}}'s specific field.
380
381<procedure>(thread-specific-set! thread obj)</procedure><br>
382
383Stores {{obj}} into the {{thread}}'s specific field.
384{{thread-specific-set!}} returns an unspecified value.
385
386     (thread-specific-set! (current-thread) "hello")  ==>  ''unspecified''
387 
388     (thread-specific (current-thread))               ==>  "hello"
389
390Alternatively, you can use
391
392     (set! (thread-specific (current-thread)) "hello")
393
394<procedure>(thread-start! thread)</procedure><br>
395
396Makes {{thread}} runnable. The {{thread}} must be a new thread.
397{{thread-start!}} returns the {{thread}}.
398
399
400     (let ((t (thread-start! (make-thread (lambda () (write 'a))))))
401       (write 'b)
402       (thread-join! t))             ==>  ''unspecified''
403                                          ''after writing'' ab ''or'' ba
404
405NOTE: It is useful to separate thread creation and thread activation to
406avoid the race condition that would occur if the created thread tries to
407examine a table in which the current thread stores the created thread. See
408the last example of {{thread-terminate!}} which contains mutually recursive
409threads.
410
411<procedure>(thread-yield!)</procedure><br>
412
413The current thread exits the running state as if its quantum had expired.
414{{thread-yield!}} returns an unspecified value.
415
416
417     ; a busy loop that avoids being too wasteful of the CPU
418 
419     (let loop ()
420       (if (mutex-lock! m 0) ; try to lock m but don't block
421           (begin
422             (display "locked mutex m")
423             (mutex-unlock! m))
424           (begin
425             (do-something-else)
426             (thread-yield!) ; relinquish rest of quantum
427             (loop))))
428
429<procedure>(thread-sleep! timeout)</procedure><br>
430
431The current thread waits until the timeout is reached. This blocks the
432thread only if {{timeout}} represents a point in the future. It is an error
433for {{timeout}} to be {{#f}}. {{thread-sleep!}} returns an unspecified
434value.
435
436
437     ; a clock with a gradual drift:
438 
439     (let loop ((x 1))
440       (thread-sleep! 1)
441       (write x)
442       (loop (+ x 1)))
443 
444     ; a clock with no drift:
445 
446     (let ((start (time->seconds (current-time)))
447       (let loop ((x 1))
448         (thread-sleep! (seconds->time (+ x start)))
449         (write x)
450         (loop (+ x 1))))
451
452<procedure>(thread-terminate! thread)</procedure><br>
453
454Causes an abnormal termination of the {{thread}}. If the {{thread}}
455is not already terminated, all mutexes owned by the {{thread}} become
456unlocked/abandoned and a "terminated thread exception" object is stored in
457the {{thread}}'s end-exception field. If {{thread}} is the current thread,
458{{thread-terminate!}} does not return. Otherwise {{thread-terminate!}}
459returns an unspecified value; the termination of the {{thread}} will occur
460before {{thread-terminate!}} returns.
461
462
463     (thread-terminate! (current-thread))  ==>  ''does not return''
464 
465     (define (amb thunk1 thunk2)
466       (let ((result #f)
467             (result-mutex (make-mutex))
468             (done-mutex (make-mutex)))
469         (letrec ((child1
470                   (make-thread
471                     (lambda ()
472                       (let ((x (thunk1)))
473                         (mutex-lock! result-mutex #f #f)
474                         (set! result x)
475                         (thread-terminate! child2)
476                         (mutex-unlock! done-mutex)))))
477                  (child2
478                   (make-thread
479                     (lambda ()
480                       (let ((x (thunk2)))
481                         (mutex-lock! result-mutex #f #f)
482                         (set! result x)
483                         (thread-terminate! child1)
484                         (mutex-unlock! done-mutex))))))
485           (mutex-lock! done-mutex #f #f)
486           (thread-start! child1)
487           (thread-start! child2)
488           (mutex-lock! done-mutex #f #f)
489           result)))
490
491
492NOTE: This operation must be used carefully because it terminates a
493thread abruptly and it is impossible for that thread to perform any kind
494of cleanup. This may be a problem if the thread is in the middle of a
495critical section where some structure has been put in an inconsistent
496state. However, another thread attempting to enter this critical
497section will raise an "abandoned mutex exception" because the mutex is
498unlocked/abandoned. This helps avoid observing an inconsistent state. Clean
499termination can be obtained by polling, as shown in the example below.
500
501
502     (define (spawn thunk)
503       (let ((t (make-thread thunk)))
504         (thread-specific-set! t #t)
505         (thread-start! t)
506         t))
507 
508     (define (stop! thread)
509       (thread-specific-set! thread #f)
510       (thread-join! thread))
511 
512     (define (keep-going?)
513       (thread-specific (current-thread)))
514 
515     (define count!
516       (let ((m (make-mutex))
517             (i 0))
518         (lambda ()
519           (mutex-lock! m)
520           (let ((x (+ i 1)))
521             (set! i x)
522             (mutex-unlock! m)
523             x))))
524 
525     (define (increment-forever!)
526       (let loop () (count!) (if (keep-going?) (loop))))
527 
528     (let ((t1 (spawn increment-forever!))
529           (t2 (spawn increment-forever!)))
530       (thread-sleep! 1)
531       (stop! t1)
532       (stop! t2)
533       (count!))  ==>  377290
534
535<procedure>(thread-join! thread [timeout [timeout-val]])</procedure><br>
536
537The current thread waits until the {{thread}} terminates (normally or
538not) or until the timeout is reached if {{timeout}} is supplied. If the
539timeout is reached, {{thread-join!}} returns {{timeout-val}} if it is
540supplied, otherwise a "join timeout exception" is raised. If the {{thread}}
541terminated normally, the content of the end-result field is returned,
542otherwise the content of the end-exception field is raised.
543
544
545     (let ((t (thread-start! (make-thread (lambda () (expt 2 100))))))
546       (do-something-else)
547       (thread-join! t))  ==>  1267650600228229401496703205376
548 
549     (let ((t (thread-start! (make-thread (lambda () (raise 123))))))
550       (do-something-else)
551       (with-exception-handler
552         (lambda (exc)
553           (if (uncaught-exception? exc)
554               (* 10 (uncaught-exception-reason exc))
555               99999))
556         (lambda ()
557           (+ 1 (thread-join! t)))))  ==>  1231
558 
559     (define thread-alive?
560       (let ((unique (list 'unique)))
561         (lambda (thread)
562           ; Note: this procedure raises an exception if
563           ; the thread terminated abnormally.
564           (eq? (thread-join! thread 0 unique) unique))))
565 
566     (define (wait-for-termination! thread)
567       (let ((eh (current-exception-handler)))
568         (with-exception-handler
569           (lambda (exc)
570             (if (not (or (terminated-thread-exception? exc)
571                          (uncaught-exception? exc)))
572                 (eh exc))) ; unexpected exceptions are handled by eh
573           (lambda ()
574             ; The following call to thread-join! will wait until the
575             ; thread terminates.  If the thread terminated normally
576             ; thread-join! will return normally.  If the thread
577             ; terminated abnormally then one of these two exceptions
578             ; is raised by thread-join!:
579             ;   - terminated thread exception
580             ;   - uncaught exception
581             (thread-join! thread)
582             #f)))) ; ignore result of thread-join!
583
584
585<procedure>(mutex? obj)</procedure><br>
586
587Returns {{#t}} if {{obj}} is a mutex, otherwise returns {{#f}}.
588
589
590     (mutex? (make-mutex))  ==>  #t
591     (mutex? 'foo)          ==>  #f
592
593<procedure>(make-mutex [name])</procedure><br>
594
595Returns a new mutex in the unlocked/not-abandoned state. The optional
596{{name}} is an arbitrary Scheme object which identifies the mutex (useful
597for debugging); it defaults to an unspecified value. The mutex's specific
598field is set to an unspecified value.
599
600
601     (make-mutex)       ==>  ''an unlocked/not-abandoned mutex''
602     (make-mutex 'foo)  ==>  ''an unlocked/not-abandoned mutex named'' foo
603
604
605<procedure>(mutex-name mutex)</procedure><br>
606
607Returns the name of the {{mutex}}.
608
609
610     (mutex-name (make-mutex 'foo))  ==>  foo
611
612
613<procedure>(mutex-specific mutex)</procedure><br>
614
615Returns the content of the {{mutex}}'s specific field.
616
617<procedure>(mutex-specific-set! mutex obj)</procedure><br>
618
619Stores {{obj}} into the {{mutex}}'s specific field. {{mutex-specific-set!}}
620returns an unspecified value.
621
622
623     (define m (make-mutex))
624     (mutex-specific-set! m "hello")  ==>  ''unspecified''
625 
626     (mutex-specific m)               ==>  "hello"
627 
628     (define (mutex-lock-recursively! mutex)
629       (if (eq? (mutex-state mutex) (current-thread))
630           (let ((n (mutex-specific mutex)))
631             (mutex-specific-set! mutex (+ n 1)))
632           (begin
633             (mutex-lock! mutex)
634             (mutex-specific-set! mutex 0))))
635 
636     (define (mutex-unlock-recursively! mutex)
637       (let ((n (mutex-specific mutex)))
638         (if (= n 0)
639             (mutex-unlock! mutex)
640             (mutex-specific-set! mutex (- n 1)))))
641
642<procedure>(mutex-state mutex)</procedure><br>
643
644Returns information about the state of the {{mutex}}. The possible results
645are:
646
647
648* '''thread T''': the {{mutex}} is in the locked/owned state and thread T is the owner of the {{mutex}}
649* '''symbol {{not-owned}}''': the {{mutex}} is in the locked/not-owned state
650* '''symbol {{abandoned}}''': the {{mutex}} is in the unlocked/abandoned state
651* '''symbol {{not-abandoned}}''': the {{mutex}} is in the unlocked/not-abandoned state
652
653
654     (mutex-state (make-mutex))  ==>  not-abandoned
655 
656     (define (thread-alive? thread)
657       (let ((mutex (make-mutex)))
658         (mutex-lock! mutex #f thread)
659         (let ((state (mutex-state mutex)))
660           (mutex-unlock! mutex) ; avoid space leak
661           (eq? state thread))))
662
663<procedure>(mutex-lock! mutex [timeout [thread]])</procedure><br>
664
665If the {{mutex}} is currently locked, the current thread waits until the
666{{mutex}} is unlocked, or until the timeout is reached if {{timeout}}
667is supplied. If the timeout is reached, {{mutex-lock!}} returns {{#f}}.
668Otherwise, the state of the {{mutex}} is changed as follows:
669
670
671* if {{thread}} is {{#f}} the {{mutex}} becomes locked/not-owned,
672* otherwise, let T be {{thread}} (or the current thread if {{thread}} is not supplied),
673** if T is terminated the {{mutex}} becomes unlocked/abandoned,
674** otherwise {{mutex}} becomes locked/owned with T as the owner.
675
676After changing the state of the {{mutex}}, an "abandoned mutex exception"
677is raised if the {{mutex}} was unlocked/abandoned before the state change,
678otherwise {{mutex-lock!}} returns {{#t}}. It is not an error if the
679{{mutex}} is owned by the current thread (but the current thread will have
680to wait).
681
682
683     ; an implementation of a mailbox object of depth one; this
684     ; implementation does not behave well in the presence of forced
685     ; thread terminations using thread-terminate! (deadlock can occur
686     ; if a thread is terminated in the middle of a put! or get! operation)
687 
688     (define (make-empty-mailbox)
689       (let ((put-mutex (make-mutex)) ; allow put! operation
690             (get-mutex (make-mutex))
691             (cell #f))
692 
693         (define (put! obj)
694           (mutex-lock! put-mutex #f #f) ; prevent put! operation
695           (set! cell obj)
696           (mutex-unlock! get-mutex)) ; allow get! operation
697 
698         (define (get!)
699           (mutex-lock! get-mutex #f #f) ; wait until object in mailbox
700           (let ((result cell))
701             (set! cell #f) ; prevent space leaks
702             (mutex-unlock! put-mutex) ; allow put! operation
703             result))
704 
705         (mutex-lock! get-mutex #f #f) ; prevent get! operation
706 
707         (lambda (msg)
708           (case msg
709             ((put!) put!)
710             ((get!) get!)
711             (else (error "unknown message"))))))
712 
713     (define (mailbox-put! m obj) ((m 'put!) obj))
714     (define (mailbox-get! m) ((m 'get!)))
715 
716     ; an alternate implementation of thread-sleep!
717 
718     (define (sleep! timeout)
719       (let ((m (make-mutex)))
720         (mutex-lock! m #f #f)
721         (mutex-lock! m timeout #f)))
722 
723     ; a procedure that waits for one of two mutexes to unlock
724 
725     (define (lock-one-of! mutex1 mutex2)
726       ; this procedure assumes that neither mutex1 or mutex2
727       ; are owned by the current thread
728       (let ((ct (current-thread))
729             (done-mutex (make-mutex)))
730         (mutex-lock! done-mutex #f #f)
731         (let ((t1 (thread-start!
732                    (make-thread
733                     (lambda ()
734                       (mutex-lock! mutex1 #f ct)
735                       (mutex-unlock! done-mutex)))))
736               (t2 (thread-start!
737                    (make-thread
738                     (lambda ()
739                       (mutex-lock! mutex2 #f ct)
740                       (mutex-unlock! done-mutex))))))
741           (mutex-lock! done-mutex #f #f)
742           (thread-terminate! t1)
743           (thread-terminate! t2)
744           (if (eq? (mutex-state mutex1) ct)
745               (begin
746                 (if (eq? (mutex-state mutex2) ct)
747                     (mutex-unlock! mutex2)) ; don't lock both
748                 mutex1)
749               mutex2))))
750
751<procedure>(mutex-unlock! mutex [condition-variable [timeout]])</procedure><br>
752
753Unlocks the {{mutex}} by making it unlocked/not-abandoned. It is not an
754error to unlock an unlocked mutex and a mutex that is owned by any thread.
755If {{condition-variable}} is supplied, the current thread is blocked
756and added to the {{condition-variable}} before unlocking {{mutex}}; the
757thread can unblock at any time but no later than when an appropriate call
758to {{condition-variable-signal!}} or {{condition-variable-broadcast!}}
759is performed (see below), and no later than the timeout (if {{timeout}}
760is supplied). If there are threads waiting to lock this {{mutex}},
761the scheduler selects a thread, the mutex becomes locked/owned or
762locked/not-owned, and the thread is unblocked. {{mutex-unlock!}} returns
763{{#f}} when the timeout is reached, otherwise it returns {{#t}}.
764
765NOTE: The reason the thread can unblock at any time (when
766{{condition-variable}} is supplied) is to allow extending this SRFI with
767primitives that force a specific blocked thread to become runnable. For
768example a primitive to interrupt a thread so that it performs a certain
769operation, whether the thread is blocked or not, may be useful to handle
770the case where the scheduler has detected a serious problem (such as a
771deadlock) and it must unblock one of the threads (such as the primordial
772thread) so that it can perform some appropriate action. After a thread
773blocked on a condition-variable has handled such an interrupt it would be
774wrong for the scheduler to return the thread to the blocked state, because
775any calls to {{condition-variable-broadcast!}} during the interrupt will
776have gone unnoticed. It is necessary for the thread to remain runnable and
777return from the call to {{mutex-unlock!}} with a result of {{#t}}.
778
779NOTE: {{mutex-unlock!}} is related to the "wait" operation on condition
780variables available in other thread systems. The main difference is that
781"wait" automatically locks {{mutex}} just after the thread is unblocked.
782This operation is not performed by {{mutex-unlock!}} and so must be
783done by an explicit call to {{mutex-lock!}}. This has the advantages
784that a different timeout and exception handler can be specified on the
785{{mutex-lock!}} and {{mutex-unlock!}} and the location of all the mutex
786operations is clearly apparent. A typical use with a condition variable is:
787
788
789     (let loop ()
790       (mutex-lock! m)
791       (if (condition-is-true?)
792           (begin
793             (do-something-when-condition-is-true)
794             (mutex-unlock! m))
795           (begin
796             (mutex-unlock! m cv)
797             (loop))))
798
799<procedure>(condition-variable? obj)</procedure><br>
800
801Returns {{#t}} if {{obj}} is a condition variable, otherwise returns
802{{#f}}.
803
804
805     (condition-variable? (make-condition-variable))  ==>  #t
806     (condition-variable? 'foo)                       ==>  #f
807
808<procedure>(make-condition-variable [name])</procedure><br>
809
810Returns a new empty condition variable. The optional {{name}} is an
811arbitrary Scheme object which identifies the condition variable (useful for
812debugging); it defaults to an unspecified value. The condition variable's
813specific field is set to an unspecified value.
814
815
816     (make-condition-variable)  ==>  ''an empty condition variable''
817
818<procedure>(condition-variable-name condition-variable)</procedure><br>
819
820Returns the name of the {{condition-variable}}.
821
822
823     (condition-variable-name (make-condition-variable 'foo))  ==>  foo
824
825<procedure>(condition-variable-specific condition-variable)</procedure><br>
826
827Returns the content of the {{condition-variable}}'s specific field.
828
829<procedure>(condition-variable-specific-set! condition-variable obj)</procedure><br>
830
831Stores {{obj}} into the {{condition-variable}}'s specific field.
832{{condition-variable-specific-set!}} returns an unspecified value.
833
834
835     (define cv (make-condition-variable))
836     (condition-variable-specific-set! cv "hello")  ==>  ''unspecified''
837 
838     (condition-variable-specific cv)               ==>  "hello"
839
840<procedure>(condition-variable-signal! condition-variable)</procedure><br>
841
842If there are threads blocked on the {{condition-variable}}, the scheduler
843selects a thread and unblocks it. {{condition-variable-signal!}} returns an
844unspecified value.
845
846
847     ; an implementation of a mailbox object of depth one; this
848     ; implementation behaves gracefully when threads are forcibly
849     ; terminated using thread-terminate! (the "abandoned mutex"
850     ; exception will be raised when a put! or get! operation is attempted
851     ; after a thread is terminated in the middle of a put! or get!
852     ; operation)
853 
854     (define (make-empty-mailbox)
855       (let ((mutex (make-mutex))
856             (put-condvar (make-condition-variable))
857             (get-condvar (make-condition-variable))
858             (full? #f)
859             (cell #f))
860 
861         (define (put! obj)
862           (mutex-lock! mutex)
863           (if full?
864               (begin
865                 (mutex-unlock! mutex put-condvar)
866                 (put! obj))
867               (begin
868                 (set! cell obj)
869                 (set! full? #t)
870                 (condition-variable-signal! get-condvar)
871                 (mutex-unlock! mutex))))
872 
873         (define (get!)
874           (mutex-lock! mutex)
875           (if (not full?)
876               (begin
877                 (mutex-unlock! mutex get-condvar)
878                 (get!))
879               (let ((result cell))
880                 (set! cell #f) ; avoid space leaks
881                 (set! full? #f)
882                 (condition-variable-signal! put-condvar)
883                 (mutex-unlock! mutex))))
884 
885         (lambda (msg)
886           (case msg
887             ((put!) put!)
888             ((get!) get!)
889             (else (error "unknown message"))))))
890 
891     (define (mailbox-put! m obj) ((m 'put!) obj))
892     (define (mailbox-get! m) ((m 'get!)))
893
894<procedure>(condition-variable-broadcast! condition-variable)</procedure><br>
895
896Unblocks all the threads blocked on the {{condition-variable}}.
897{{condition-variable-broadcast!}} returns an unspecified value.
898
899
900     (define (make-semaphore n)
901       (vector n (make-mutex) (make-condition-variable)))
902 
903     (define (semaphore-wait! sema)
904       (mutex-lock! (vector-ref sema 1))
905       (let ((n (vector-ref sema 0)))
906         (if (> n 0)
907             (begin
908               (vector-set! sema 0 (- n 1))
909               (mutex-unlock! (vector-ref sema 1)))
910             (begin
911               (mutex-unlock! (vector-ref sema 1) (vector-ref sema 2))
912               (semaphore-wait! sema))))
913 
914     (define (semaphore-signal-by! sema increment)
915       (mutex-lock! (vector-ref sema 1))
916       (let ((n (+ (vector-ref sema 0) increment)))
917         (vector-set! sema 0 n)
918         (if (> n 0)
919             (condition-variable-broadcast! (vector-ref sema 2)))
920         (mutex-unlock! (vector-ref sema 1))))
921
922<procedure>(current-time)</procedure><br>
923
924Returns the time object corresponding to the current time.
925
926
927     (current-time)  ==>  ''a time object''
928
929<procedure>(time? obj)</procedure><br>
930
931Returns {{#t}} if {{obj}} is a time object, otherwise returns {{#f}}.
932
933
934     (time? (current-time))  ==>  #t
935     (time? 123)             ==>  #f
936
937<procedure>(time->seconds time)</procedure><br>
938
939Converts the time object {{time}} into an exact or inexact real number
940representing the number of seconds elapsed since some implementation
941dependent reference point.
942
943
944     (time->seconds (current-time))  ==>  955039784.928075
945
946<procedure>(seconds->time x)</procedure><br>
947
948Converts into a time object the exact or inexact real number {{x}}
949representing the number of seconds elapsed since some implementation
950dependent reference point.
951
952
953     (seconds->time (+ 10 (time->seconds (current-time)))
954        ==>  ''a time object representing 10 seconds in the future''
955
956
957<procedure>(current-exception-handler)</procedure><br>
958
959Returns the current exception handler.
960
961
962     (current-exception-handler)  ==>  ''a procedure''
963
964<procedure>(with-exception-handler handler thunk)</procedure><br>
965
966Returns the result(s) of calling {{thunk}} with no arguments. The
967{{handler}}, which must be a procedure, is installed as the current
968exception handler in the dynamic environment in effect during the call to
969{{thunk}}.
970
971
972     (with-exception-handler
973       list
974       current-exception-handler)  ==>  ''the procedure'' list
975
976<procedure>(raise obj)</procedure><br>
977
978Calls the current exception handler with {{obj}} as the single argument.
979{{obj}} may be any Scheme object.
980
981
982     (define (f n)
983       (if (< n 0) (raise "negative arg") (sqrt n))))
984 
985     (define (g)
986       (call-with-current-continuation
987         (lambda (return)
988           (with-exception-handler
989             (lambda (exc)
990               (return
991                 (if (string? exc)
992                     (string-append "error: " exc)
993                     "unknown error")))
994             (lambda ()
995               (write (f 4.))
996               (write (f -1.))
997               (write (f 9.)))))))
998 
999     (g)  ==>  ''writes'' 2. ''and returns'' "error: negative arg"
1000
1001
1002<procedure>(join-timeout-exception? obj)</procedure><br>
1003
1004Returns {{#t}} if {{obj}} is a "join timeout exception" object, otherwise
1005returns {{#f}}. A join timeout exception is raised when {{thread-join!}} is
1006called, the timeout is reached and no {{timeout-val}} is supplied.
1007
1008<procedure>(abandoned-mutex-exception? obj)</procedure><br>
1009
1010Returns {{#t}} if {{obj}} is an "abandoned mutex exception" object,
1011otherwise returns {{#f}}. An abandoned mutex exception is raised when the
1012current thread locks a mutex that was owned by a thread which terminated
1013(see {{mutex-lock!}}).
1014
1015<procedure>(terminated-thread-exception? obj)</procedure><br>
1016
1017Returns {{#t}} if {{obj}} is a "terminated thread exception" object,
1018otherwise returns {{#f}}. A terminated thread exception is raised when
1019{{thread-join!}} is called and the target thread has terminated as a result
1020of a call to {{thread-terminate!}}.
1021
1022<procedure>(uncaught-exception? obj)</procedure><br>
1023
1024Returns {{#t}} if {{obj}} is an "uncaught exception" object, otherwise
1025returns {{#f}}. An uncaught exception is raised when {{thread-join!}} is
1026called and the target thread has terminated because it raised an exception
1027that called the initial exception handler of that thread.
1028
1029<procedure>(uncaught-exception-reason exc)</procedure><br>
1030
1031{{exc}} must be an "uncaught exception" object.
1032{{uncaught-exception-reason}} returns the object which was passed to the
1033initial exception handler of that thread.
1034
1035
1036---
1037Previous: [[Unit srfi-14]]
1038
1039Next: [[Unit srfi-69]]
Note: See TracBrowser for help on using the repository browser.