/* [<][>][^][v][top][bottom][index][help] */
1 /*
2 * gauche/port.h - Port inline macros
3 *
4 * Copyright (c) 2000-2004 Shiro Kawai, All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 *
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 *
17 * 3. Neither the name of the authors nor the names of its contributors
18 * may be used to endorse or promote products derived from this
19 * software without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
27 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
28 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
29 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
30 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
31 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 *
33 * $Id: port.h,v 1.11 2004/10/17 10:29:39 shirok Exp $
34 */
35
36 /*
37 * Defines several macros that inlines primitive port opertaions.
38 * Used in Gauche internally. Not for general use.
39 */
40
41 /* Mutex of ports:
42 * SRFI-18 requires the system to serialize access to ports.
43 * I need to implement a few tricks to avoid this requirement
44 * from affecting performance too much.
45 *
46 * Each port has a recursive lock, that is, the same thread can
47 * lock the port many times; the port will be fully unlocked
48 * when the thread calls unlock as many times as it called lock.
49 *
50 * The port functions may call-back Scheme code or other Gauche
51 * C API that may take arbitrarily long time to execute, and may
52 * raise an error. It prevents us from using simple mutex; we
53 * need to use CVs, and need to save dynamic context to revert
54 * lock state in case an error is thrown during processing.
55 *
56 * If implemented naively it costs too much, since in most
57 * cases the port operation is trivial; such as fetching some
58 * bytes from memory and incrementing a counter. Only in some
59 * occasions the operation involves system calls or calling other
60 * Gauche C functions. In the following macro, mutex call is done
61 * only when the caller hasn't been locked the port.
62 *
63 * As you see, PORT_UNLOCK doesn't do any mutex ops. I count on
64 * that the pointer assignment is atomic; if so, the race condition
65 * won't happen. If not, Boehm GC won't work anyway.
66 *
67 * Furthermore, some port functions have 'unsafe' mode, which
68 * should be called when the caller is sure that the thread
69 * already locked the port.
70 */
71
72 /* TODO: what if a thread having a lock of the port died before unlocking it?
73 * Currently, PORT_LOCK checks if the lock holder is terminated or not.
74 * However, the dead thread may have left the port state inconsistent.
75 * I need to set the cancellation points carefully...
76 */
77
78 #define PORT_LOCK(p, vm) \
79 do { \
80 if (!(p->flags&SCM_PORT_PRIVATE)) { \
81 if (p->lockOwner != vm) { \
82 (void)SCM_INTERNAL_MUTEX_LOCK(p->mutex); \
83 while (p->lockOwner != NULL) { \
84 if (p->lockOwner->state == SCM_VM_TERMINATED) { \
85 break; \
86 } \
87 (void)SCM_INTERNAL_COND_WAIT(p->cv, p->mutex); \
88 } \
89 p->lockOwner = vm; \
90 p->lockCount = 0; /* for safety */ \
91 (void)SCM_INTERNAL_MUTEX_UNLOCK(p->mutex); \
92 } else { \
93 p->lockCount++; \
94 } \
95 } \
96 } while (0)
97
98 /* Assumes the calling thread has the lock */
99 #define PORT_UNLOCK(p) \
100 do { \
101 if (!(p->flags&SCM_PORT_PRIVATE)) { \
102 if (--p->lockCount <= 0) { \
103 p->lockOwner = NULL; \
104 (void)SCM_INTERNAL_COND_SIGNAL(p->cv); \
105 } \
106 } \
107 } while (0)
108
109 #define PORT_SAFE_CALL(p, call) \
110 do { \
111 if (!(p->flags&SCM_PORT_PRIVATE)) { \
112 SCM_UNWIND_PROTECT { \
113 call; \
114 } SCM_WHEN_ERROR { \
115 PORT_UNLOCK(p); \
116 SCM_NEXT_HANDLER; \
117 } SCM_END_PROTECT; \
118 } else { \
119 call; \
120 } \
121 } while (0)
122
123 #define PORT_LOCKED(p, vm) \
124 (((p)->flags&SCM_PORT_PRIVATE)||((p)->lockOwner == (vm)))
125
126 /* Should be used in the constructor of provate ports.
127 Mark the port locked by vm, so that it can be used exclusively by
128 the vm. */
129
130 #define PORT_PRELOCK(p, vm) \
131 do { \
132 p->lockOwner = vm; \
133 p->lockCount = 1; \
134 } while (0)
135