1 /* 2 * portP.h - Port private API 3 * 4 * Copyright (c) 2013-2020 Shiro Kawai <shiro@acm.org> 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 34 #ifndef GAUCHE_PRIV_PORTP_H 35 #define GAUCHE_PRIV_PORTP_H 36 37 #include "gauche/priv/writerP.h" 38 39 /*================================================================ 40 * Real port structure 41 * 42 * ScmPort is actually ScmPortImpl. 43 */ 44 45 /* 46 * Regardless of the port type, the port structure caches at most 47 * one character, in order to realize `peek-char' (Scheme) or `Ungetc' (C) 48 * operation. 'scratch', 'scrcnt', and 'ungotten' fields are used for 49 * that purpose, and outside routine shouldn't touch these fields. 50 * See portapi.c for the detailed semantics. 51 * 52 * Supporting custom ports (r6rs, srfi-181) complicates the interaction 53 * between an 'ungotten' character and port position. The position of custom 54 * ports can be arbitrary object, and it is opaque outside of the custom 55 * port implementation. So the port layer can't adjust the position. 56 * Instead, peek-char/byte has to prefetch the position to remember. 57 */ 58 59 typedef struct ScmPortImplRec { 60 SCM_PORT_HEADER; 61 62 char scratch[SCM_CHAR_MAX_BYTES]; /* incomplete buffer */ 63 64 ScmChar ungotten; /* ungotten character. 65 SCM_CHAR_INVALID if empty. */ 66 ScmObj savedPos; /* When we peek-char/byte on custom port, 67 we need to cache the position. */ 68 69 ScmInternalFastlock lock; /* for port mutex */ 70 ScmVM *lockOwner; /* for port mutex; owner of the lock */ 71 int lockCount; /* for port mutex; # of recursive locks */ 72 73 ScmWriteState *writeState; /* used internally */ 74 75 /* Input counters. these doesn't take account of ungetting and 76 seeking: Ungetting doesn't affect those counters (you can think 77 that ungetting are handled above the counting layer). 78 Seeking invalidates counters; if you seek, the values of the counters 79 become bogus. 80 We don't have character counter, since it is difficult to track 81 (read-line uses byte read; see Scm_ReadLine in portapi.c). 82 */ 83 ScmSize line; /* line counter */ 84 ScmSize bytes; /* byte counter */ 85 86 /* The source or the sink of the port. Use specialized accessor 87 functions to retrieve one of those union members. */ 88 union { 89 ScmPortBuffer buf; /* buffered port */ 90 ScmPortInputString istr; 91 ScmDString ostr; /* output string port */ 92 ScmPortVTable vt; /* virtual port */ 93 } src; 94 95 /* Port attibutes. Use Scm_PortAttr* API to access. */ 96 ScmObj attrs; 97 98 } ScmPortImpl; 99 100 #define P_(p) ((ScmPortImpl*)(p)) 101 102 /*================================================================ 103 * Some private APIs 104 */ 105 106 SCM_EXTERN void Scm__InstallCodingAwarePortHook(ScmPort *(*)(ScmPort*, const char*)); 107 108 /* Windows-specific initialization */ 109 #if defined(GAUCHE_WINDOWS) 110 void Scm__SetupPortsForWindows(int has_console); 111 #endif /*defined(GAUCHE_WINDOWS)*/ 112 113 #define PORT_WALKER_P(port) \ 114 (SCM_PORTP(port) && (SCM_PORT(port)->flags & SCM_PORT_WALKING)) 115 116 #define PORT_WRITESS_P(port) \ 117 (SCM_PORTP(port) && (SCM_PORT(port)->flags & SCM_PORT_WRITESS)) 118 119 #define PORT_RECURSIVE_P(port) \ 120 (P_(port)->writeState != NULL) 121 122 #define PORT_LOCK_OWNER_P(port, vm) \ 123 (P_(port)->lockOwner == (vm)) 124 125 /* Internal intreface to retrieve src member. 126 For public use, we have Scm_PortBufferStruct() etc. */ 127 #define PORT_BUF(port) (&P_(port)->src.buf) 128 #define PORT_ISTR(port) (&P_(port)->src.istr) 129 #define PORT_OSTR(port) (&P_(port)->src.ostr) 130 #define PORT_VT(port) (&P_(port)->src.vt) 131 132 /*================================================================ 133 * Locking the ports 134 * 135 * Since most of the public APIs locks the ports, you don't usually 136 * need to lock the ports by yourself. The following macros 137 * shouldn't be used casually. 138 * 139 * Port locking overhead is critical to the I/O performance. 140 * The following macros are designed carefully so that it minimizes 141 * the call to the system-level lock primitives, under the assumption 142 * that port access never conflicts in the performance critical code. 143 * (It doesn't make much sense for multiple threads to write to the 144 * same port, since the outputs are mixed in unpredictable way---except 145 * a casual debug print to stderr, but I don't believe performance 146 * critical part does that.) 147 * 148 * The port's lock state is kept in a single pointer, port->lockOwner. 149 * It points to the owner of the port, or NULL if the port is unlocked. 150 * Unlocking the port is a single atomic operation, port->lockOwner = NULL, 151 * hence PORT_UNLOCK doesn't need mutex to do that. 152 * 153 * To lock the port, the thread needs to grab a system-level lock 154 * (spinlock if available, mutex otherwise) to check the lockOwner 155 * pointer. If the port is locked, the thread yields CPU and 156 * try again later. 157 * 158 * It is possible that lockOwner slot changes its value to NULL during 159 * a thread is trying to lock the port, since PORT_UNLOCK doesn't obtain 160 * the system-level lock. If it happens, the thread trying to lock 161 * the port would wait extra timeslice. Not a big deal. 162 * 163 * Note that we cannot use a condition variable to let the locking thread 164 * wait on it. If we use CV, unlocking becomes two-step operation 165 * (set lockOwner to NULL, and call cond_signal), so it is no longer 166 * atomic. We would need to get system-level lock in PORT_UNLOCK as well. 167 */ 168 169 /* Lock a port P. Can perform recursive lock. */ 170 #define PORT_LOCK(p, vm) \ 171 do { \ 172 if (P_(p)->lockOwner != vm) { \ 173 for (;;) { \ 174 ScmVM* owner__; \ 175 (void)SCM_INTERNAL_FASTLOCK_LOCK(P_(p)->lock); \ 176 owner__ = P_(p)->lockOwner; \ 177 if (owner__ == NULL \ 178 || (owner__->state == SCM_VM_TERMINATED)) { \ 179 P_(p)->lockOwner = vm; \ 180 P_(p)->lockCount = 1; \ 181 } \ 182 (void)SCM_INTERNAL_FASTLOCK_UNLOCK(P_(p)->lock); \ 183 if (P_(p)->lockOwner == vm) break; \ 184 Scm_YieldCPU(); \ 185 } \ 186 } else { \ 187 P_(p)->lockCount++; \ 188 } \ 189 } while (0) 190 191 /* Unlock a port P. Assumes the calling thread has the lock */ 192 #define PORT_UNLOCK(p) \ 193 do { \ 194 if (--P_(p)->lockCount <= 0) { \ 195 SCM_INTERNAL_SYNC(); \ 196 P_(p)->lockOwner = NULL; \ 197 } \ 198 } while (0) 199 200 /* Should be used while P is locked by calling thread. 201 Evaluate C statement CALL, making sure the port is unlocked in case 202 CALL raises an error. 203 CLEANUP is a C stmt called no matter CALL succeeds or not. 204 TODO: we may be able to utilize SCM_PORT_PRIVATE flag to avoid 205 SCM_UNWIND_PROTECT overhead. */ 206 #define PORT_SAFE_CALL(p, call, cleanup) \ 207 do { \ 208 SCM_UNWIND_PROTECT { \ 209 call; \ 210 cleanup; \ 211 } SCM_WHEN_ERROR { \ 212 cleanup; \ 213 PORT_UNLOCK(p); \ 214 SCM_NEXT_HANDLER; \ 215 } SCM_END_PROTECT; \ 216 } while (0) 217 218 #define PORT_LOCKED(p, vm) ((P_(p)->lockOwner == (vm))) 219 220 /* Should be used in the constructor of private ports. 221 Mark the port locked by vm, so that it can be used exclusively by 222 the vm. */ 223 224 #define PORT_PRELOCK(p, vm) \ 225 do { \ 226 P_(p)->lockOwner = vm; \ 227 P_(p)->lockCount = 1; \ 228 } while (0) 229 230 231 #endif /*GAUCHE_PRIV_PORTP_H*/ 232